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