coins.cpp raw

   1  // Copyright (c) 2011-2022 The Limenka 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 Limenka 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 limenka 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|account", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Set to null to only account for trusted transactions, or \"*\" to account for all transactions."},
 171                      {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include incoming transactions confirmed at least this many times. (Requires dummy=\"*\")"},
 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>("dummy")};
 198      if (dummy_value && *dummy_value != "*") {
 199          throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
 200      }
 201  
 202      const auto min_depth{dummy_value ? self.Arg<int>("minconf") : 0};
 203  
 204      bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
 205  
 206      bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
 207  
 208      if (dummy_value) {
 209          if (avoid_reuse) throw JSONRPCError(RPC_INVALID_PARAMETER, "getbalance avoid_reuse flag is not supported if dummy is set to \"*\"");
 210          isminefilter filter = ISMINE_SPENDABLE;
 211          if (include_watchonly) filter = filter | ISMINE_WATCH_ONLY;
 212          return ValueFromAmount(pwallet->GetLegacyBalance(filter, min_depth));
 213      }
 214  
 215      if (!request.params[1].isNull()) {
 216          throw JSONRPCError(RPC_INVALID_PARAMETER, "getbalance minconf option is only currently supported if dummy is set to \"*\"");
 217      }
 218  
 219      const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
 220  
 221      return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
 222  },
 223      };
 224  }
 225  
 226  RPCHelpMan getunconfirmedbalance()
 227  {
 228      return RPCHelpMan{"getunconfirmedbalance",
 229                  "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
 230                  {},
 231                  RPCResult{RPCResult::Type::NUM, "", "The balance"},
 232                  RPCExamples{""},
 233          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 234  {
 235      const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
 236      if (!pwallet) return UniValue::VNULL;
 237  
 238      // Make sure the results are valid at least up to the most recent block
 239      // the user could have gotten from another RPC command prior to now
 240      pwallet->BlockUntilSyncedToCurrentChain();
 241  
 242      LOCK(pwallet->cs_wallet);
 243  
 244      return ValueFromAmount(GetBalance(*pwallet).m_mine_untrusted_pending);
 245  },
 246      };
 247  }
 248  
 249  RPCHelpMan lockunspent()
 250  {
 251      return RPCHelpMan{"lockunspent",
 252                  "\nUpdates list of temporarily unspendable outputs.\n"
 253                  "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
 254                  "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
 255                  "A locked transaction output will not be chosen by automatic coin selection, when spending limenkas.\n"
 256                  "Manually selected coins are automatically unlocked.\n"
 257                  "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
 258                  "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
 259                  "(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
 260                  "Also see the listunspent call\n",
 261                  {
 262                      {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
 263                      {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
 264                          {
 265                              {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
 266                                  {
 267                                      {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
 268                                      {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
 269                                  },
 270                              },
 271                          },
 272                      },
 273                      {"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."},
 274                  },
 275                  RPCResult{
 276                      RPCResult::Type::BOOL, "", "Whether the command was successful or not"
 277                  },
 278                  RPCExamples{
 279              "\nList the unspent transactions\n"
 280              + HelpExampleCli("listunspent", "") +
 281              "\nLock an unspent transaction\n"
 282              + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
 283              "\nList the locked transactions\n"
 284              + HelpExampleCli("listlockunspent", "") +
 285              "\nUnlock the transaction again\n"
 286              + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
 287              "\nLock the transaction persistently in the wallet database\n"
 288              + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
 289              "\nAs a JSON-RPC call\n"
 290              + HelpExampleRpc("lockunspent", "false, [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":1}]")
 291                  },
 292          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 293  {
 294      std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
 295      if (!pwallet) return UniValue::VNULL;
 296  
 297      // Make sure the results are valid at least up to the most recent block
 298      // the user could have gotten from another RPC command prior to now
 299      pwallet->BlockUntilSyncedToCurrentChain();
 300  
 301      LOCK(pwallet->cs_wallet);
 302  
 303      bool fUnlock = request.params[0].get_bool();
 304  
 305      const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
 306  
 307      if (request.params[1].isNull()) {
 308          if (fUnlock) {
 309              if (!pwallet->UnlockAllCoins())
 310                  throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
 311          }
 312          return true;
 313      }
 314  
 315      const UniValue& output_params = request.params[1].get_array();
 316  
 317      // Create and validate the COutPoints first.
 318  
 319      std::vector<COutPoint> outputs;
 320      outputs.reserve(output_params.size());
 321  
 322      for (unsigned int idx = 0; idx < output_params.size(); idx++) {
 323          const UniValue& o = output_params[idx].get_obj();
 324  
 325          RPCTypeCheckObj(o,
 326              {
 327                  {"txid", UniValueType(UniValue::VSTR)},
 328                  {"vout", UniValueType(UniValue::VNUM)},
 329              });
 330  
 331          const Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));
 332          const int nOutput = o.find_value("vout").getInt<int>();
 333          if (nOutput < 0) {
 334              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
 335          }
 336  
 337          const COutPoint outpt(txid, nOutput);
 338  
 339          const auto it = pwallet->mapWallet.find(outpt.hash);
 340          if (it == pwallet->mapWallet.end()) {
 341              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
 342          }
 343  
 344          const CWalletTx& trans = it->second;
 345  
 346          if (outpt.n >= trans.tx->vout.size()) {
 347              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
 348          }
 349  
 350          if (pwallet->IsSpent(outpt)) {
 351              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
 352          }
 353  
 354          const bool is_locked = pwallet->IsLockedCoin(outpt);
 355  
 356          if (fUnlock && !is_locked) {
 357              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
 358          }
 359  
 360          if (!fUnlock && is_locked && !persistent) {
 361              throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
 362          }
 363  
 364          outputs.push_back(outpt);
 365      }
 366  
 367      std::unique_ptr<WalletBatch> batch = nullptr;
 368      // Unlock is always persistent
 369      if (fUnlock || persistent) batch = std::make_unique<WalletBatch>(pwallet->GetDatabase());
 370  
 371      // Atomically set (un)locked status for the outputs.
 372      for (const COutPoint& outpt : outputs) {
 373          if (fUnlock) {
 374              if (!pwallet->UnlockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
 375          } else {
 376              if (!pwallet->LockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
 377          }
 378      }
 379  
 380      return true;
 381  },
 382      };
 383  }
 384  
 385  RPCHelpMan listlockunspent()
 386  {
 387      return RPCHelpMan{"listlockunspent",
 388                  "\nReturns list of temporarily unspendable outputs.\n"
 389                  "See the lockunspent call to lock and unlock transactions for spending.\n",
 390                  {},
 391                  RPCResult{
 392                      RPCResult::Type::ARR, "", "",
 393                      {
 394                          {RPCResult::Type::OBJ, "", "",
 395                          {
 396                              {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
 397                              {RPCResult::Type::NUM, "vout", "The vout value"},
 398                          }},
 399                      }
 400                  },
 401                  RPCExamples{
 402              "\nList the unspent transactions\n"
 403              + HelpExampleCli("listunspent", "") +
 404              "\nLock an unspent transaction\n"
 405              + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
 406              "\nList the locked transactions\n"
 407              + HelpExampleCli("listlockunspent", "") +
 408              "\nUnlock the transaction again\n"
 409              + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
 410              "\nAs a JSON-RPC call\n"
 411              + HelpExampleRpc("listlockunspent", "")
 412                  },
 413          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 414  {
 415      const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
 416      if (!pwallet) return UniValue::VNULL;
 417  
 418      LOCK(pwallet->cs_wallet);
 419  
 420      std::vector<COutPoint> vOutpts;
 421      pwallet->ListLockedCoins(vOutpts);
 422  
 423      UniValue ret(UniValue::VARR);
 424  
 425      for (const COutPoint& outpt : vOutpts) {
 426          UniValue o(UniValue::VOBJ);
 427  
 428          o.pushKV("txid", outpt.hash.GetHex());
 429          o.pushKV("vout", (int)outpt.n);
 430          ret.push_back(std::move(o));
 431      }
 432  
 433      return ret;
 434  },
 435      };
 436  }
 437  
 438  RPCHelpMan getbalances()
 439  {
 440      return RPCHelpMan{
 441          "getbalances",
 442          "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
 443          {},
 444          RPCResult{
 445              RPCResult::Type::OBJ, "", "",
 446              {
 447                  {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
 448                  {
 449                      {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
 450                      {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
 451                      {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
 452                      {RPCResult::Type::STR, "precise", "trusted balance in lambda with full 128-bit precision (sub-satoshi fractions and confidential outputs included)"},
 453                      {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)"},
 454                  }},
 455                  {RPCResult::Type::OBJ, "watchonly", /*optional=*/true, "watchonly balances (not present if wallet does not watch anything)",
 456                  {
 457                      {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
 458                      {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
 459                      {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
 460                  }},
 461                  RESULT_LAST_PROCESSED_BLOCK,
 462              }
 463              },
 464          RPCExamples{
 465              HelpExampleCli("getbalances", "") +
 466              HelpExampleRpc("getbalances", "")},
 467          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 468  {
 469      const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
 470      if (!rpc_wallet) return UniValue::VNULL;
 471      const CWallet& wallet = *rpc_wallet;
 472  
 473      // Make sure the results are valid at least up to the most recent block
 474      // the user could have gotten from another RPC command prior to now
 475      wallet.BlockUntilSyncedToCurrentChain();
 476  
 477      LOCK(wallet.cs_wallet);
 478  
 479      const auto bal = GetBalance(wallet);
 480      // Full 128-bit precision (sub-satoshi + confidential receipts) as a
 481      // string: JSON numbers cannot carry 26 decimal places.
 482      const std::string balance_precise = AttosatsToString(wallet.GetPreciseBalanceAttosats());
 483      UniValue balances{UniValue::VOBJ};
 484      {
 485          UniValue balances_mine{UniValue::VOBJ};
 486          balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
 487          balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
 488          balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
 489          balances_mine.pushKV("precise", balance_precise);
 490          if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
 491              // If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
 492              // the total balance, and then subtract bal to get the reused address balance.
 493              const auto full_bal = GetBalance(wallet, 0, false);
 494              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));
 495          }
 496          balances.pushKV("mine", std::move(balances_mine));
 497      }
 498      auto spk_man = wallet.GetLegacyScriptPubKeyMan();
 499      if (spk_man && spk_man->HaveWatchOnly()) {
 500          UniValue balances_watchonly{UniValue::VOBJ};
 501          balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
 502          balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
 503          balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
 504          balances.pushKV("watchonly", std::move(balances_watchonly));
 505      }
 506  
 507      AppendLastProcessedBlock(balances, wallet);
 508      return balances;
 509  },
 510      };
 511  }
 512  
 513  RPCHelpMan listunspent()
 514  {
 515      return RPCHelpMan{
 516                  "listunspent",
 517                  "\nReturns array of unspent transaction outputs\n"
 518                  "with between minconf and maxconf (inclusive) confirmations.\n"
 519                  "Optionally filter to only include txouts paid to specified addresses.\n",
 520                  {
 521                      {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
 522                      {"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
 523                      {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The limenka addresses to filter",
 524                          {
 525                              {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "limenka address"},
 526                          },
 527                      },
 528                      {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include outputs that are not safe to spend\n"
 529                                "See description of \"safe\" attribute below."},
 530                      {"query_options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
 531                          {
 532                              {"minimumAmount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)}, "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
 533                              {"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
 534                              {"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
 535                              {"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
 536                              {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase UTXOs"}
 537                          },
 538                          RPCArgOptions{.oneline_description="query_options"}},
 539                  },
 540                  RPCResult{
 541                      RPCResult::Type::ARR, "", "",
 542                      {
 543                          {RPCResult::Type::OBJ, "", "",
 544                          {
 545                              {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
 546                              {RPCResult::Type::NUM, "vout", "the vout value"},
 547                              {RPCResult::Type::STR, "address", /*optional=*/true, "the limenka address"},
 548                              {RPCResult::Type::STR, "label", /*optional=*/true, "The associated label, or \"\" for the default label"},
 549                              {RPCResult::Type::STR, "scriptPubKey", "the output script"},
 550                              {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
 551                              {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
 552                              {RPCResult::Type::NUM, "ancestorcount", /*optional=*/true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
 553                              {RPCResult::Type::NUM, "ancestorsize", /*optional=*/true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
 554                              {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)"},
 555                              {RPCResult::Type::STR_HEX, "redeemScript", /*optional=*/true, "The redeem script if the output script is P2SH"},
 556                              {RPCResult::Type::STR, "witnessScript", /*optional=*/true, "witness script if the output script is P2WSH or P2SH-P2WSH"},
 557                              {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
 558                              {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
 559                              {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)"},
 560                              {RPCResult::Type::STR, "desc", /*optional=*/true, "(only when solvable) A descriptor for spending this output"},
 561                              {RPCResult::Type::ARR, "parent_descs", /*optional=*/false, "List of parent descriptors for the output script of this coin.", {
 562                                  {RPCResult::Type::STR, "desc", "The descriptor string."},
 563                              }},
 564                              {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
 565                                                              "from outside keys and unconfirmed replacement transactions are considered unsafe\n"
 566                                                              "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
 567                          }},
 568                      }
 569                  },
 570                  RPCExamples{
 571                      HelpExampleCli("listunspent", "")
 572              + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
 573              + HelpExampleRpc("listunspent", "6, 9999999, [\"" + EXAMPLE_ADDRESS[0] + "\",\"" + EXAMPLE_ADDRESS[1] + "\"]")
 574              + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
 575              + HelpExampleRpc("listunspent", "6, 9999999, [], true, { \"minimumAmount\": 0.005 } ")
 576                  },
 577          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 578  {
 579      const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
 580      if (!pwallet) return UniValue::VNULL;
 581  
 582      int nMinDepth = 1;
 583      if (!request.params[0].isNull()) {
 584          nMinDepth = request.params[0].getInt<int>();
 585      }
 586  
 587      int nMaxDepth = 9999999;
 588      if (!request.params[1].isNull()) {
 589          nMaxDepth = request.params[1].getInt<int>();
 590      }
 591  
 592      std::set<CTxDestination> destinations;
 593      if (!request.params[2].isNull()) {
 594          UniValue inputs = request.params[2].get_array();
 595          for (unsigned int idx = 0; idx < inputs.size(); idx++) {
 596              const UniValue& input = inputs[idx];
 597              CTxDestination dest = DecodeDestination(input.get_str());
 598              if (!IsValidDestination(dest)) {
 599                  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Limenka address: ") + input.get_str());
 600              }
 601              if (!destinations.insert(dest).second) {
 602                  throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
 603              }
 604          }
 605      }
 606  
 607      bool include_unsafe = true;
 608      if (!request.params[3].isNull()) {
 609          include_unsafe = request.params[3].get_bool();
 610      }
 611  
 612      CoinFilterParams filter_coins;
 613      filter_coins.min_amount = 0;
 614  
 615      if (!request.params[4].isNull()) {
 616          const UniValue& options = request.params[4].get_obj();
 617  
 618          RPCTypeCheckObj(options,
 619              {
 620                  {"minimumAmount", UniValueType()},
 621                  {"maximumAmount", UniValueType()},
 622                  {"minimumSumAmount", UniValueType()},
 623                  {"maximumCount", UniValueType(UniValue::VNUM)},
 624                  {"include_immature_coinbase", UniValueType(UniValue::VBOOL)}
 625              },
 626              true, true);
 627  
 628          if (options.exists("minimumAmount"))
 629              filter_coins.min_amount = AmountFromValue(options["minimumAmount"]);
 630  
 631          if (options.exists("maximumAmount"))
 632              filter_coins.max_amount = AmountFromValue(options["maximumAmount"]);
 633  
 634          if (options.exists("minimumSumAmount"))
 635              filter_coins.min_sum_amount = AmountFromValue(options["minimumSumAmount"]);
 636  
 637          if (options.exists("maximumCount"))
 638              filter_coins.max_count = options["maximumCount"].getInt<int64_t>();
 639  
 640          if (options.exists("include_immature_coinbase")) {
 641              filter_coins.include_immature_coinbase = options["include_immature_coinbase"].get_bool();
 642          }
 643      }
 644  
 645      // Make sure the results are valid at least up to the most recent block
 646      // the user could have gotten from another RPC command prior to now
 647      pwallet->BlockUntilSyncedToCurrentChain();
 648  
 649      UniValue results(UniValue::VARR);
 650      std::vector<COutput> vecOutputs;
 651      {
 652          CCoinControl cctl;
 653          cctl.m_avoid_address_reuse = false;
 654          cctl.m_min_depth = nMinDepth;
 655          cctl.m_max_depth = nMaxDepth;
 656          cctl.m_include_unsafe_inputs = include_unsafe;
 657          LOCK(pwallet->cs_wallet);
 658          vecOutputs = AvailableCoinsListUnspent(*pwallet, &cctl, filter_coins).All();
 659      }
 660  
 661      LOCK(pwallet->cs_wallet);
 662  
 663      const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
 664  
 665      for (const COutput& out : vecOutputs) {
 666          CTxDestination address;
 667          const CScript& scriptPubKey = out.txout.scriptPubKey;
 668          bool fValidAddress = ExtractDestination(scriptPubKey, address);
 669          bool reused = avoid_reuse && pwallet->IsSpentKey(scriptPubKey);
 670  
 671          if (destinations.size() && (!fValidAddress || !destinations.count(address)))
 672              continue;
 673  
 674          UniValue entry(UniValue::VOBJ);
 675          entry.pushKV("txid", out.outpoint.hash.GetHex());
 676          entry.pushKV("vout", (int)out.outpoint.n);
 677  
 678          if (fValidAddress) {
 679              entry.pushKV("address", EncodeDestination(address));
 680  
 681              const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
 682              if (address_book_entry) {
 683                  entry.pushKV("label", address_book_entry->GetLabel());
 684              }
 685  
 686              std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
 687              if (provider) {
 688                  if (scriptPubKey.IsPayToScriptHash()) {
 689                      const CScriptID hash = ToScriptID(std::get<ScriptHash>(address));
 690                      CScript redeemScript;
 691                      if (provider->GetCScript(hash, redeemScript)) {
 692                          entry.pushKV("redeemScript", HexStr(redeemScript));
 693                          // Now check if the redeemScript is actually a P2WSH script
 694                          CTxDestination witness_destination;
 695                          if (redeemScript.IsPayToWitnessScriptHash()) {
 696                              bool extracted = ExtractDestination(redeemScript, witness_destination);
 697                              CHECK_NONFATAL(extracted);
 698                              // Also return the witness script
 699                              const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination);
 700                              CScriptID id{RIPEMD160(whash)};
 701                              CScript witnessScript;
 702                              if (provider->GetCScript(id, witnessScript)) {
 703                                  entry.pushKV("witnessScript", HexStr(witnessScript));
 704                              }
 705                          }
 706                      }
 707                  } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
 708                      const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(address);
 709                      CScriptID id{RIPEMD160(whash)};
 710                      CScript witnessScript;
 711                      if (provider->GetCScript(id, witnessScript)) {
 712                          entry.pushKV("witnessScript", HexStr(witnessScript));
 713                      }
 714                  }
 715              }
 716          }
 717  
 718          entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
 719          entry.pushKV("amount", ValueFromAmount(out.txout.nValue));
 720          entry.pushKV("confirmations", out.depth);
 721          if (!out.depth) {
 722              size_t ancestor_count, descendant_count, ancestor_size;
 723              CAmount ancestor_fees;
 724              pwallet->chain().getTransactionAncestry(out.outpoint.hash, ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
 725              if (ancestor_count) {
 726                  entry.pushKV("ancestorcount", uint64_t(ancestor_count));
 727                  entry.pushKV("ancestorsize", uint64_t(ancestor_size));
 728                  entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
 729              }
 730          }
 731          entry.pushKV("spendable", out.spendable);
 732          entry.pushKV("solvable", out.solvable);
 733          if (out.solvable) {
 734              std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
 735              if (provider) {
 736                  auto descriptor = InferDescriptor(scriptPubKey, *provider);
 737                  entry.pushKV("desc", descriptor->ToString());
 738              }
 739          }
 740          PushParentDescriptors(*pwallet, scriptPubKey, entry);
 741          if (avoid_reuse) entry.pushKV("reused", reused);
 742          entry.pushKV("safe", out.safe);
 743          results.push_back(std::move(entry));
 744      }
 745  
 746      return results;
 747  },
 748      };
 749  }
 750  } // namespace wallet
 751