coins.cpp raw

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