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 204 : static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
19 : {
20 204 : std::vector<CScript> output_scripts;
21 :
22 204 : if (by_label) {
23 : // Get the set of addresses assigned to label
24 118 : std::string label = LabelFromValue(params[0]);
25 118 : 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 2982 : RPCHelpMan getreceivedbyaddress()
84 : {
85 5964 : return RPCHelpMan{"getreceivedbyaddress",
86 2982 : "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
87 14910 : {
88 2982 : {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Dash address for transactions."},
89 2982 : {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
90 2982 : {"addlocked", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include transactions locked via InstantSend."},
91 2982 : {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
92 : },
93 2982 : RPCResult{
94 2982 : RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
95 : },
96 2982 : RPCExamples{
97 : "\nThe amount from transactions with at least 1 confirmation\n"
98 2982 : + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
99 : "\nThe amount including unconfirmed transactions, zero confirmations\n"
100 2982 : + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0") +
101 : "\nThe amount with at least 6 confirmations\n"
102 2982 : + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
103 : "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
104 2982 : + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6 true true") +
105 : "\nAs a JSON-RPC call\n"
106 2982 : + HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
107 : },
108 3068 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
109 : {
110 86 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
111 86 : 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 86 : pwallet->BlockUntilSyncedToCurrentChain();
116 :
117 86 : LOCK(pwallet->cs_wallet);
118 :
119 86 : return ValueFromAmount(GetReceived(*pwallet, request.params, /*by_label=*/false));
120 86 : },
121 : };
122 0 : }
123 :
124 3014 : RPCHelpMan getreceivedbylabel()
125 : {
126 6028 : return RPCHelpMan{"getreceivedbylabel",
127 3014 : "\nReturns the total amount received by addresses with <label> in transactions with specified minimum number of confirmations.\n",
128 15070 : {
129 3014 : {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
130 3014 : {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
131 3014 : {"addlocked", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include transactions locked via InstantSend."},
132 3014 : {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
133 : },
134 3014 : RPCResult{
135 3014 : RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
136 : },
137 3014 : RPCExamples{
138 : "\nAmount received by the default label with at least 1 confirmation\n"
139 3014 : + HelpExampleCli("getreceivedbylabel", "\"\"") +
140 : "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
141 3014 : + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
142 : "\nThe amount with at least 6 confirmations\n"
143 3014 : + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
144 : "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
145 3014 : + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6 true true") +
146 : "\nAs a JSON-RPC call\n"
147 3014 : + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
148 : },
149 3132 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
150 : {
151 118 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
152 118 : 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 118 : pwallet->BlockUntilSyncedToCurrentChain();
157 :
158 118 : LOCK(pwallet->cs_wallet);
159 :
160 118 : return ValueFromAmount(GetReceived(*pwallet, request.params, /*by_label=*/true));
161 118 : },
162 : };
163 0 : }
164 :
165 2908 : RPCHelpMan listaddressbalances()
166 : {
167 5816 : return RPCHelpMan{"listaddressbalances",
168 2908 : "\nLists addresses of this wallet and their balances\n",
169 5816 : {
170 2908 : {"minamount", RPCArg::Type::NUM, RPCArg::Default{0}, "Minimum balance in " + CURRENCY_UNIT + " an address should have to be shown in the list"},
171 : },
172 2908 : RPCResult{
173 2908 : RPCResult::Type::OBJ_DYN, "", "Balances of addresses",
174 5816 : {
175 2908 : {RPCResult::Type::STR_AMOUNT, "address", "the amount in " + CURRENCY_UNIT},
176 : }
177 : },
178 2908 : RPCExamples{
179 2908 : HelpExampleCli("listaddressbalances", "")
180 2908 : + HelpExampleCli("listaddressbalances", "10")
181 2908 : + HelpExampleRpc("listaddressbalances", "")
182 2908 : + HelpExampleRpc("listaddressbalances", "10")
183 : },
184 2920 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
185 : {
186 12 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
187 12 : if (!pwallet) return UniValue::VNULL;
188 :
189 12 : LOCK(pwallet->cs_wallet);
190 :
191 12 : CAmount nMinAmount = 0;
192 12 : if (!request.params[0].isNull())
193 8 : nMinAmount = AmountFromValue(request.params[0]);
194 :
195 12 : if (nMinAmount < 0)
196 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
197 :
198 12 : UniValue jsonBalances(UniValue::VOBJ);
199 12 : std::map<CTxDestination, CAmount> balances = GetAddressBalances(*pwallet);
200 30 : for (auto& balance : balances)
201 18 : if (balance.second >= nMinAmount)
202 12 : jsonBalances.pushKV(EncodeDestination(balance.first), ValueFromAmount(balance.second));
203 :
204 12 : return jsonBalances;
205 12 : },
206 : };
207 0 : }
208 :
209 :
210 4851 : RPCHelpMan getbalance()
211 : {
212 9702 : return RPCHelpMan{"getbalance",
213 4851 : "\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 29106 : {
217 4851 : {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
218 4851 : {"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
219 4851 : {"addlocked", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include transactions locked via InstantSend in the wallet's balance."},
220 4851 : {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also include balance in watch-only addresses (see 'importaddress')"},
221 4851 : {"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 4851 : RPCResult{
224 4851 : RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
225 : },
226 4851 : RPCExamples{
227 : "\nThe total amount in the wallet with 0 or more confirmations\n"
228 4851 : + HelpExampleCli("getbalance", "") +
229 : "\nThe total amount in the wallet with at least 6 confirmations\n"
230 4851 : + HelpExampleCli("getbalance", "\"*\" 6") +
231 : "\nAs a JSON-RPC call\n"
232 4851 : + HelpExampleRpc("getbalance", "\"*\", 6")
233 : },
234 6806 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
235 : {
236 1955 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
237 1955 : 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 1955 : pwallet->BlockUntilSyncedToCurrentChain();
242 :
243 1955 : LOCK(pwallet->cs_wallet);
244 :
245 1955 : const auto dummy_value{self.MaybeArg<std::string>(0)};
246 1955 : if (dummy_value && *dummy_value != "*") {
247 4 : throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
248 : }
249 :
250 1951 : int min_depth = 0;
251 1951 : if (!request.params[1].isNull()) {
252 68 : min_depth = request.params[1].getInt<int>();
253 68 : }
254 :
255 1951 : const UniValue& addlocked = request.params[2];
256 1951 : bool fAddLocked = false;
257 1951 : if (!addlocked.isNull()) {
258 8 : fAddLocked = addlocked.get_bool();
259 8 : }
260 :
261 1951 : bool include_watchonly = ParseIncludeWatchonly(request.params[3], *pwallet);
262 :
263 1951 : bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[4]);
264 1951 : const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse, fAddLocked);
265 :
266 1951 : return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
267 1959 : },
268 : };
269 0 : }
270 :
271 2904 : RPCHelpMan getunconfirmedbalance()
272 : {
273 5808 : return RPCHelpMan{"getunconfirmedbalance",
274 2904 : "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
275 2904 : {},
276 2904 : RPCResult{RPCResult::Type::NUM, "", "The balance"},
277 2904 : RPCExamples{""},
278 2912 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
279 : {
280 8 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
281 8 : 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 8 : pwallet->BlockUntilSyncedToCurrentChain();
286 :
287 8 : LOCK(pwallet->cs_wallet);
288 :
289 8 : return ValueFromAmount(GetBalance(*pwallet).m_mine_untrusted_pending);
290 8 : },
291 : };
292 0 : }
293 :
294 3232 : RPCHelpMan lockunspent()
295 : {
296 6464 : return RPCHelpMan{"lockunspent",
297 3232 : "\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 12928 : {
307 3232 : {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
308 6464 : {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
309 6464 : {
310 6464 : {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
311 9696 : {
312 3232 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
313 3232 : {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
314 : },
315 : },
316 : },
317 : },
318 3232 : {"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 3232 : RPCResult{
321 3232 : RPCResult::Type::BOOL, "", "Whether the command was successful or not"
322 : },
323 3232 : RPCExamples{
324 : "\nList the unspent transactions\n"
325 3232 : + HelpExampleCli("listunspent", "") +
326 : "\nLock an unspent transaction\n"
327 3232 : + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
328 : "\nList the locked transactions\n"
329 3232 : + HelpExampleCli("listlockunspent", "") +
330 : "\nUnlock the transaction again\n"
331 3232 : + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
332 : "\nLock the transaction persistently in the wallet database\n"
333 3232 : + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
334 : "\nAs a JSON-RPC call\n"
335 3232 : + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
336 : },
337 3568 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
338 : {
339 336 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
340 336 : 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 336 : pwallet->BlockUntilSyncedToCurrentChain();
345 :
346 336 : LOCK(pwallet->cs_wallet);
347 :
348 336 : bool fUnlock = request.params[0].get_bool();
349 :
350 336 : const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
351 :
352 336 : if (request.params[1].isNull()) {
353 27 : if (fUnlock) {
354 27 : if (!pwallet->UnlockAllCoins())
355 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
356 27 : }
357 27 : return true;
358 : }
359 :
360 309 : const UniValue& output_params = request.params[1].get_array();
361 :
362 : // Create and validate the COutPoints first.
363 :
364 309 : std::vector<COutPoint> outputs;
365 309 : outputs.reserve(output_params.size());
366 :
367 1054 : for (unsigned int idx = 0; idx < output_params.size(); idx++) {
368 799 : const UniValue& o = output_params[idx].get_obj();
369 :
370 1598 : RPCTypeCheckObj(o,
371 2397 : {
372 799 : {"txid", UniValueType(UniValue::VSTR)},
373 799 : {"vout", UniValueType(UniValue::VNUM)},
374 : });
375 :
376 799 : const uint256 txid(ParseHashO(o, "txid"));
377 787 : const int nOutput{o.find_value("vout").getInt<int>()};
378 787 : if (nOutput < 0) {
379 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
380 : }
381 :
382 787 : const COutPoint outpt(txid, nOutput);
383 :
384 787 : const auto it = pwallet->mapWallet.find(outpt.hash);
385 787 : if (it == pwallet->mapWallet.end()) {
386 6 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
387 : }
388 :
389 781 : const CWalletTx& trans = it->second;
390 :
391 781 : if (outpt.n >= trans.tx->vout.size()) {
392 6 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
393 : }
394 :
395 775 : if (pwallet->IsSpent(outpt)) {
396 6 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
397 : }
398 :
399 769 : const bool is_locked = pwallet->IsLockedCoin(outpt);
400 :
401 769 : if (fUnlock && !is_locked) {
402 6 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
403 : }
404 :
405 763 : if (!fUnlock && is_locked && !persistent) {
406 18 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
407 : }
408 :
409 745 : outputs.push_back(outpt);
410 745 : }
411 :
412 255 : std::unique_ptr<WalletBatch> batch = nullptr;
413 : // Unlock is always persistent
414 255 : if (fUnlock || persistent) batch = std::make_unique<WalletBatch>(pwallet->GetDatabase());
415 :
416 : // Atomically set (un)locked status for the outputs.
417 1000 : for (const COutPoint& outpt : outputs) {
418 745 : if (fUnlock) {
419 24 : if (!pwallet->UnlockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
420 24 : } else {
421 721 : if (!pwallet->LockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
422 : }
423 : }
424 :
425 255 : return true;
426 378 : },
427 : };
428 0 : }
429 :
430 3004 : RPCHelpMan listlockunspent()
431 : {
432 6008 : return RPCHelpMan{"listlockunspent",
433 3004 : "\nReturns list of temporarily unspendable outputs.\n"
434 : "See the lockunspent call to lock and unlock transactions for spending.\n",
435 3004 : {},
436 3004 : RPCResult{
437 3004 : RPCResult::Type::ARR, "", "",
438 6008 : {
439 6008 : {RPCResult::Type::OBJ, "", "",
440 9012 : {
441 3004 : {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
442 3004 : {RPCResult::Type::NUM, "vout", "The vout value"},
443 : }},
444 : }
445 : },
446 3004 : RPCExamples{
447 : "\nList the unspent transactions\n"
448 3004 : + HelpExampleCli("listunspent", "") +
449 : "\nLock an unspent transaction\n"
450 3004 : + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
451 : "\nList the locked transactions\n"
452 3004 : + HelpExampleCli("listlockunspent", "") +
453 : "\nUnlock the transaction again\n"
454 3004 : + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
455 : "\nAs a JSON-RPC call\n"
456 3004 : + HelpExampleRpc("listlockunspent", "")
457 : },
458 3112 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
459 : {
460 108 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
461 108 : if (!pwallet) return UniValue::VNULL;
462 :
463 108 : LOCK(pwallet->cs_wallet);
464 :
465 108 : UniValue ret(UniValue::VARR);
466 172 : for (const COutPoint& outpt : pwallet->ListLockedCoins()) {
467 64 : UniValue o(UniValue::VOBJ);
468 64 : o.pushKV("txid", outpt.hash.GetHex());
469 64 : o.pushKV("vout", (int)outpt.n);
470 64 : ret.push_back(o);
471 64 : }
472 :
473 108 : return ret;
474 108 : },
475 : };
476 0 : }
477 :
478 4496 : RPCHelpMan getbalances()
479 : {
480 4496 : return RPCHelpMan{
481 4496 : "getbalances",
482 4496 : "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
483 4496 : {},
484 4496 : RPCResult{
485 4496 : RPCResult::Type::OBJ, "", "",
486 13488 : {
487 8992 : {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
488 26976 : {
489 4496 : {RPCResult::Type::STR_AMOUNT, "trusted", " trusted balance (outputs created by the wallet or confirmed outputs)"},
490 4496 : {RPCResult::Type::STR_AMOUNT, "untrusted_pending", " untrusted pending balance (outputs created by others that are in the mempool)"},
491 4496 : {RPCResult::Type::STR_AMOUNT, "immature", " balance from immature coinbase outputs"},
492 4496 : {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 4496 : {RPCResult::Type::STR_AMOUNT, "coinjoin", " CoinJoin balance (outputs with enough rounds created by the wallet via mixing)"},
494 : }},
495 8992 : {RPCResult::Type::OBJ, "watchonly", /*optional=*/true, "watchonly balances (not present if wallet does not watch anything)",
496 17984 : {
497 4496 : {RPCResult::Type::STR_AMOUNT, "trusted", " trusted balance (outputs created by the wallet or confirmed outputs)"},
498 4496 : {RPCResult::Type::STR_AMOUNT, "untrusted_pending", " untrusted pending balance (outputs created by others that are in the mempool)"},
499 4496 : {RPCResult::Type::STR_AMOUNT, "immature", " balance from immature coinbase outputs"},
500 : }},
501 4496 : RESULT_LAST_PROCESSED_BLOCK,
502 : },
503 : },
504 4496 : RPCExamples{
505 8992 : HelpExampleCli("getbalances", "") +
506 4496 : HelpExampleRpc("getbalances", "")},
507 6068 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
508 : {
509 1572 : const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
510 1572 : if (!rpc_wallet) return UniValue::VNULL;
511 1572 : 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 1572 : wallet.BlockUntilSyncedToCurrentChain();
516 :
517 1572 : LOCK(wallet.cs_wallet);
518 :
519 1572 : const auto bal = GetBalance(wallet);
520 1572 : UniValue balances{UniValue::VOBJ};
521 : {
522 1572 : UniValue balances_mine{UniValue::VOBJ};
523 1572 : balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
524 1572 : balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
525 1572 : balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
526 1572 : 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 32 : const auto full_bal = GetBalance(wallet, 0, false);
530 32 : 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 32 : }
532 1572 : balances_mine.pushKV("coinjoin", ValueFromAmount(bal.m_anonymized));
533 1572 : balances.pushKV("mine", balances_mine);
534 1572 : }
535 1572 : auto spk_man = wallet.GetLegacyScriptPubKeyMan();
536 1572 : if (spk_man && spk_man->HaveWatchOnly()) {
537 32 : UniValue balances_watchonly{UniValue::VOBJ};
538 32 : balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
539 32 : balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
540 32 : balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
541 32 : balances.pushKV("watchonly", balances_watchonly);
542 32 : }
543 :
544 1572 : AppendLastProcessedBlock(balances, wallet);
545 1572 : return balances;
546 1572 : },
547 : };
548 0 : }
549 :
550 3447 : RPCHelpMan listunspent()
551 : {
552 3447 : return RPCHelpMan{
553 3447 : "listunspent",
554 3447 : "\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 20682 : {
558 3447 : {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
559 3447 : {"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
560 6894 : {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The Dash addresses to filter",
561 6894 : {
562 3447 : {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Dash address"},
563 : },
564 : },
565 3447 : {"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 6894 : {"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
568 20682 : {
569 3447 : {"minimumAmount", RPCArg::Type::AMOUNT, RPCArg::Default{0}, "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
570 3447 : {"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
571 3447 : {"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
572 3447 : {"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
573 3447 : {"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 3447 : "query_options"},
578 : },
579 3447 : RPCResult{
580 3447 : RPCResult::Type::ARR, "", "",
581 6894 : {
582 6894 : {RPCResult::Type::OBJ, "", "",
583 62046 : {
584 3447 : {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
585 3447 : {RPCResult::Type::NUM, "vout", "the vout value"},
586 3447 : {RPCResult::Type::STR, "address", /*optional=*/true, "the Dash address"},
587 3447 : {RPCResult::Type::STR, "label", /*optional=*/true, "The associated label, or \"\" for the default label"},
588 3447 : {RPCResult::Type::STR, "scriptPubKey", "the script key"},
589 3447 : {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
590 3447 : {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
591 3447 : {RPCResult::Type::NUM, "ancestorcount", /*optional=*/true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
592 3447 : {RPCResult::Type::NUM, "ancestorsize", /*optional=*/true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
593 3447 : {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 3447 : {RPCResult::Type::STR_HEX, "redeemScript", /*optional=*/true, "The redeemScript if scriptPubKey is P2SH"},
595 3447 : {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
596 3447 : {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
597 3447 : {RPCResult::Type::STR, "desc", /*optional=*/true, "(only when solvable) A descriptor for spending this output"},
598 3447 : {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 3447 : {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 3447 : {RPCResult::Type::NUM, "coinjoin_rounds", "The number of CoinJoin rounds"},
603 : }},
604 : }},
605 3447 : RPCExamples{
606 3447 : HelpExampleCli("listunspent", "")
607 3447 : + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
608 3447 : + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
609 3447 : + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
610 3447 : + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
611 : },
612 3998 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
613 : {
614 551 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
615 551 : if (!pwallet) return UniValue::VNULL;
616 :
617 551 : int nMinDepth = 1;
618 551 : if (!request.params[0].isNull()) {
619 209 : nMinDepth = request.params[0].getInt<int>();
620 209 : }
621 :
622 551 : int nMaxDepth = 9999999;
623 551 : if (!request.params[1].isNull()) {
624 98 : nMaxDepth = request.params[1].getInt<int>();
625 98 : }
626 :
627 551 : std::set<CTxDestination> destinations;
628 551 : if (!request.params[2].isNull()) {
629 33 : UniValue inputs = request.params[2].get_array();
630 66 : for (unsigned int idx = 0; idx < inputs.size(); idx++) {
631 33 : const UniValue& input = inputs[idx];
632 33 : CTxDestination dest = DecodeDestination(input.get_str());
633 33 : if (!IsValidDestination(dest)) {
634 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + input.get_str());
635 : }
636 33 : if (!destinations.insert(dest).second) {
637 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
638 : }
639 33 : }
640 33 : }
641 :
642 551 : bool include_unsafe = true;
643 551 : if (!request.params[3].isNull()) {
644 86 : include_unsafe = request.params[3].get_bool();
645 86 : }
646 :
647 551 : CAmount nMinimumAmount = 0;
648 551 : CAmount nMaximumAmount = MAX_MONEY;
649 551 : CAmount nMinimumSumAmount = MAX_MONEY;
650 551 : uint64_t nMaximumCount = 0;
651 551 : CCoinControl coinControl(CoinType::ALL_COINS);
652 :
653 551 : if (!request.params[4].isNull()) {
654 96 : const UniValue& options = request.params[4].get_obj();
655 :
656 : // Note: Keep this vector up to date with the options processed below
657 96 : const std::vector<std::string> vecOptions {
658 96 : "minimumAmount",
659 96 : "maximumAmount",
660 96 : "minimumSumAmount",
661 96 : "maximumCount",
662 96 : "coinType"
663 : };
664 :
665 192 : for (const auto& key : options.getKeys()) {
666 96 : 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 96 : if (options.exists("minimumAmount"))
672 24 : nMinimumAmount = AmountFromValue(options["minimumAmount"]);
673 :
674 96 : if (options.exists("maximumAmount"))
675 0 : nMaximumAmount = AmountFromValue(options["maximumAmount"]);
676 :
677 96 : if (options.exists("minimumSumAmount"))
678 0 : nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
679 :
680 96 : if (options.exists("maximumCount"))
681 0 : nMaximumCount = options["maximumCount"].getInt<int64_t>();
682 :
683 96 : if (options.exists("coinType")) {
684 72 : int64_t nCoinType = options["coinType"].getInt<int64_t>();
685 :
686 72 : 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 72 : coinControl.nCoinType = static_cast<CoinType>(nCoinType);
691 72 : }
692 96 : }
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 551 : pwallet->BlockUntilSyncedToCurrentChain();
697 :
698 551 : UniValue results(UniValue::VARR);
699 551 : std::vector<COutput> vecOutputs;
700 : {
701 551 : coinControl.m_avoid_address_reuse = false;
702 551 : coinControl.m_min_depth = nMinDepth;
703 551 : coinControl.m_max_depth = nMaxDepth;
704 551 : coinControl.m_include_unsafe_inputs = include_unsafe;
705 :
706 551 : LOCK(pwallet->cs_wallet);
707 551 : vecOutputs = AvailableCoinsListUnspent(*pwallet, &coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount).all();
708 551 : }
709 :
710 551 : LOCK(pwallet->cs_wallet);
711 :
712 551 : const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
713 :
714 13545 : for (const COutput& out : vecOutputs) {
715 12994 : CTxDestination address;
716 12994 : const CScript& scriptPubKey = out.txout.scriptPubKey;
717 12994 : bool fValidAddress = ExtractDestination(scriptPubKey, address);
718 13042 : bool reused = avoid_reuse && pwallet->IsSpentKey(scriptPubKey);
719 :
720 12994 : if (destinations.size() && (!fValidAddress || !destinations.count(address)))
721 1591 : continue;
722 :
723 11403 : UniValue entry(UniValue::VOBJ);
724 11403 : entry.pushKV("txid", out.outpoint.hash.GetHex());
725 11403 : entry.pushKV("vout", (int)out.outpoint.n);
726 :
727 11403 : if (fValidAddress) {
728 11403 : entry.pushKV("address", EncodeDestination(address));
729 :
730 11403 : const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
731 11403 : if (address_book_entry) {
732 11063 : entry.pushKV("label", address_book_entry->GetLabel());
733 11063 : }
734 :
735 11403 : std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
736 11403 : if (provider) {
737 11403 : if (scriptPubKey.IsPayToScriptHash()) {
738 650 : const CScriptID& hash = CScriptID(std::get<ScriptHash>(address));
739 650 : CScript redeemScript;
740 650 : if (provider->GetCScript(hash, redeemScript)) {
741 23 : entry.pushKV("redeemScript", HexStr(redeemScript));
742 23 : }
743 650 : }
744 11403 : }
745 11403 : }
746 :
747 11403 : entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
748 11403 : entry.pushKV("amount", ValueFromAmount(out.txout.nValue));
749 11403 : entry.pushKV("confirmations", out.depth);
750 11403 : if (!out.depth) {
751 : size_t ancestor_count, descendant_count, ancestor_size;
752 : CAmount ancestor_fees;
753 131 : pwallet->chain().getTransactionAncestry(out.outpoint.hash, ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
754 131 : if (ancestor_count) {
755 131 : entry.pushKV("ancestorcount", uint64_t(ancestor_count));
756 131 : entry.pushKV("ancestorsize", uint64_t(ancestor_size));
757 131 : entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
758 131 : }
759 131 : }
760 11403 : entry.pushKV("spendable", out.spendable);
761 11403 : entry.pushKV("solvable", out.solvable);
762 11403 : if (out.solvable) {
763 10771 : std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
764 10771 : if (provider) {
765 10771 : auto descriptor = InferDescriptor(scriptPubKey, *provider);
766 10771 : entry.pushKV("desc", descriptor->ToString());
767 10771 : }
768 10771 : }
769 11403 : if (avoid_reuse) entry.pushKV("reused", reused);
770 11403 : entry.pushKV("safe", out.safe);
771 11403 : entry.pushKV("coinjoin_rounds", pwallet->GetRealOutpointCoinJoinRounds(out.outpoint));
772 11403 : results.push_back(entry);
773 11403 : }
774 :
775 551 : return results;
776 551 : },
777 : };
778 0 : }
779 : } // namespace wallet
|