aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Dobson <dobsonsa68@gmail.com>2021-12-01 16:09:30 +1300
committerSamuel Dobson <dobsonsa68@gmail.com>2021-12-08 11:45:19 +1300
commit9ce521a61bb7db3c881fbb3534472a60985e19d6 (patch)
treeca8754f5ae34589e8cd87e7835020707a7dbc8d0
parent7b45f5c0591935ef195fa4a8a7bbc38c7d7c5a76 (diff)
MOVEONLY: Move balance and utxo RPCs to coins.cpp
-rw-r--r--src/Makefile.am1
-rw-r--r--src/wallet/rpc/coins.cpp733
-rw-r--r--src/wallet/rpcwallet.cpp729
3 files changed, 744 insertions, 719 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b1483e3b3f..272848ddaa 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -412,6 +412,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/receive.cpp \
wallet/rpc/addresses.cpp \
wallet/rpc/backup.cpp \
+ wallet/rpc/coins.cpp \
wallet/rpc/encrypt.cpp \
wallet/rpc/signmessage.cpp \
wallet/rpc/transactions.cpp \
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
new file mode 100644
index 0000000000..326f0f088b
--- /dev/null
+++ b/src/wallet/rpc/coins.cpp
@@ -0,0 +1,733 @@
+// Copyright (c) 2011-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <core_io.h>
+#include <key_io.h>
+#include <rpc/util.h>
+#include <util/moneystr.h>
+#include <wallet/coincontrol.h>
+#include <wallet/receive.h>
+#include <wallet/rpc/util.h>
+#include <wallet/spend.h>
+#include <wallet/wallet.h>
+
+#include <univalue.h>
+
+
+static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+{
+ std::set<CTxDestination> address_set;
+
+ if (by_label) {
+ // Get the set of addresses assigned to label
+ std::string label = LabelFromValue(params[0]);
+ address_set = wallet.GetLabelAddresses(label);
+ } else {
+ // Get the address
+ CTxDestination dest = DecodeDestination(params[0].get_str());
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
+ }
+ CScript script_pub_key = GetScriptForDestination(dest);
+ if (!wallet.IsMine(script_pub_key)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
+ }
+ address_set.insert(dest);
+ }
+
+ // Minimum confirmations
+ int min_depth = 1;
+ if (!params[1].isNull())
+ min_depth = params[1].get_int();
+
+ const bool include_immature_coinbase{params[2].isNull() ? false : params[2].get_bool()};
+
+ // Excluding coinbase outputs is deprecated
+ // It can be enabled by setting deprecatedrpc=exclude_coinbase
+ const bool include_coinbase{!wallet.chain().rpcEnableDeprecated("exclude_coinbase")};
+
+ if (include_immature_coinbase && !include_coinbase) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "include_immature_coinbase is incompatible with deprecated exclude_coinbase");
+ }
+
+ // Tally
+ CAmount amount = 0;
+ for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) {
+ const CWalletTx& wtx = wtx_pair.second;
+ int depth{wallet.GetTxDepthInMainChain(wtx)};
+ if (depth < min_depth
+ // Coinbase with less than 1 confirmation is no longer in the main chain
+ || (wtx.IsCoinBase() && (depth < 1 || !include_coinbase))
+ || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)
+ || !wallet.chain().checkFinalTx(*wtx.tx)) {
+ continue;
+ }
+
+ for (const CTxOut& txout : wtx.tx->vout) {
+ CTxDestination address;
+ if (ExtractDestination(txout.scriptPubKey, address) && wallet.IsMine(address) && address_set.count(address)) {
+ amount += txout.nValue;
+ }
+ }
+ }
+
+ return amount;
+}
+
+
+RPCHelpMan getreceivedbyaddress()
+{
+ return RPCHelpMan{"getreceivedbyaddress",
+ "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
+ {
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
+ {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
+ },
+ RPCResult{
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
+ },
+ RPCExamples{
+ "\nThe amount from transactions with at least 1 confirmation\n"
+ + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
+ "\nThe amount including unconfirmed transactions, zero confirmations\n"
+ + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0") +
+ "\nThe amount with at least 6 confirmations\n"
+ + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
+ "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
+ + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6 true") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
+ LOCK(pwallet->cs_wallet);
+
+ return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ false));
+},
+ };
+}
+
+
+RPCHelpMan getreceivedbylabel()
+{
+ return RPCHelpMan{"getreceivedbylabel",
+ "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
+ {
+ {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
+ {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
+ },
+ RPCResult{
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
+ },
+ RPCExamples{
+ "\nAmount received by the default label with at least 1 confirmation\n"
+ + HelpExampleCli("getreceivedbylabel", "\"\"") +
+ "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
+ + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
+ "\nThe amount with at least 6 confirmations\n"
+ + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
+ "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
+ + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6 true") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6, true")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
+ LOCK(pwallet->cs_wallet);
+
+ return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ true));
+},
+ };
+}
+
+
+RPCHelpMan getbalance()
+{
+ return RPCHelpMan{"getbalance",
+ "\nReturns the total available balance.\n"
+ "The available balance is what the wallet considers currently spendable, and is\n"
+ "thus affected by options which limit spendability such as -spendzeroconfchange.\n",
+ {
+ {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
+ {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also include balance in watch-only addresses (see 'importaddress')"},
+ {"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."},
+ },
+ RPCResult{
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
+ },
+ RPCExamples{
+ "\nThe total amount in the wallet with 0 or more confirmations\n"
+ + HelpExampleCli("getbalance", "") +
+ "\nThe total amount in the wallet with at least 6 confirmations\n"
+ + HelpExampleCli("getbalance", "\"*\" 6") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("getbalance", "\"*\", 6")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
+ LOCK(pwallet->cs_wallet);
+
+ const UniValue& dummy_value = request.params[0];
+ if (!dummy_value.isNull() && dummy_value.get_str() != "*") {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
+ }
+
+ int min_depth = 0;
+ if (!request.params[1].isNull()) {
+ min_depth = request.params[1].get_int();
+ }
+
+ bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
+
+ bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
+
+ const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
+
+ return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
+},
+ };
+}
+
+RPCHelpMan getunconfirmedbalance()
+{
+ return RPCHelpMan{"getunconfirmedbalance",
+ "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
+ {},
+ RPCResult{RPCResult::Type::NUM, "", "The balance"},
+ RPCExamples{""},
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
+ LOCK(pwallet->cs_wallet);
+
+ return ValueFromAmount(GetBalance(*pwallet).m_mine_untrusted_pending);
+},
+ };
+}
+
+RPCHelpMan lockunspent()
+{
+ return RPCHelpMan{"lockunspent",
+ "\nUpdates list of temporarily unspendable outputs.\n"
+ "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
+ "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
+ "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
+ "Manually selected coins are automatically unlocked.\n"
+ "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
+ "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
+ "(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
+ "Also see the listunspent call\n",
+ {
+ {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
+ {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
+ {
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ },
+ },
+ },
+ },
+ {"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."},
+ },
+ RPCResult{
+ RPCResult::Type::BOOL, "", "Whether the command was successful or not"
+ },
+ RPCExamples{
+ "\nList the unspent transactions\n"
+ + HelpExampleCli("listunspent", "") +
+ "\nLock an unspent transaction\n"
+ + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
+ "\nList the locked transactions\n"
+ + HelpExampleCli("listlockunspent", "") +
+ "\nUnlock the transaction again\n"
+ + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
+ "\nLock the transaction persistently in the wallet database\n"
+ + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
+ LOCK(pwallet->cs_wallet);
+
+ RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
+
+ bool fUnlock = request.params[0].get_bool();
+
+ const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
+
+ if (request.params[1].isNull()) {
+ if (fUnlock) {
+ if (!pwallet->UnlockAllCoins())
+ throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
+ }
+ return true;
+ }
+
+ RPCTypeCheckArgument(request.params[1], UniValue::VARR);
+
+ const UniValue& output_params = request.params[1];
+
+ // Create and validate the COutPoints first.
+
+ std::vector<COutPoint> outputs;
+ outputs.reserve(output_params.size());
+
+ for (unsigned int idx = 0; idx < output_params.size(); idx++) {
+ const UniValue& o = output_params[idx].get_obj();
+
+ RPCTypeCheckObj(o,
+ {
+ {"txid", UniValueType(UniValue::VSTR)},
+ {"vout", UniValueType(UniValue::VNUM)},
+ });
+
+ const uint256 txid(ParseHashO(o, "txid"));
+ const int nOutput = find_value(o, "vout").get_int();
+ if (nOutput < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
+ }
+
+ const COutPoint outpt(txid, nOutput);
+
+ const auto it = pwallet->mapWallet.find(outpt.hash);
+ if (it == pwallet->mapWallet.end()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
+ }
+
+ const CWalletTx& trans = it->second;
+
+ if (outpt.n >= trans.tx->vout.size()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
+ }
+
+ if (pwallet->IsSpent(outpt.hash, outpt.n)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
+ }
+
+ const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n);
+
+ if (fUnlock && !is_locked) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
+ }
+
+ if (!fUnlock && is_locked && !persistent) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
+ }
+
+ outputs.push_back(outpt);
+ }
+
+ std::unique_ptr<WalletBatch> batch = nullptr;
+ // Unlock is always persistent
+ if (fUnlock || persistent) batch = std::make_unique<WalletBatch>(pwallet->GetDatabase());
+
+ // Atomically set (un)locked status for the outputs.
+ for (const COutPoint& outpt : outputs) {
+ if (fUnlock) {
+ if (!pwallet->UnlockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
+ } else {
+ if (!pwallet->LockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
+ }
+ }
+
+ return true;
+},
+ };
+}
+
+RPCHelpMan listlockunspent()
+{
+ return RPCHelpMan{"listlockunspent",
+ "\nReturns list of temporarily unspendable outputs.\n"
+ "See the lockunspent call to lock and unlock transactions for spending.\n",
+ {},
+ RPCResult{
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
+ {RPCResult::Type::NUM, "vout", "The vout value"},
+ }},
+ }
+ },
+ RPCExamples{
+ "\nList the unspent transactions\n"
+ + HelpExampleCli("listunspent", "") +
+ "\nLock an unspent transaction\n"
+ + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
+ "\nList the locked transactions\n"
+ + HelpExampleCli("listlockunspent", "") +
+ "\nUnlock the transaction again\n"
+ + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
+ "\nAs a JSON-RPC call\n"
+ + HelpExampleRpc("listlockunspent", "")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ LOCK(pwallet->cs_wallet);
+
+ std::vector<COutPoint> vOutpts;
+ pwallet->ListLockedCoins(vOutpts);
+
+ UniValue ret(UniValue::VARR);
+
+ for (const COutPoint& outpt : vOutpts) {
+ UniValue o(UniValue::VOBJ);
+
+ o.pushKV("txid", outpt.hash.GetHex());
+ o.pushKV("vout", (int)outpt.n);
+ ret.push_back(o);
+ }
+
+ return ret;
+},
+ };
+}
+
+RPCHelpMan getbalances()
+{
+ return RPCHelpMan{
+ "getbalances",
+ "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
+ {},
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
+ {
+ {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
+ {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
+ {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)"},
+ }},
+ {RPCResult::Type::OBJ, "watchonly", /* optional */ true, "watchonly balances (not present if wallet does not watch anything)",
+ {
+ {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
+ {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
+ }},
+ }
+ },
+ RPCExamples{
+ HelpExampleCli("getbalances", "") +
+ HelpExampleRpc("getbalances", "")},
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
+ if (!rpc_wallet) return NullUniValue;
+ const CWallet& wallet = *rpc_wallet;
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ wallet.BlockUntilSyncedToCurrentChain();
+
+ LOCK(wallet.cs_wallet);
+
+ const auto bal = GetBalance(wallet);
+ UniValue balances{UniValue::VOBJ};
+ {
+ UniValue balances_mine{UniValue::VOBJ};
+ balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
+ balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
+ balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
+ // If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
+ // the total balance, and then subtract bal to get the reused address balance.
+ const auto full_bal = GetBalance(wallet, 0, false);
+ 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));
+ }
+ balances.pushKV("mine", balances_mine);
+ }
+ auto spk_man = wallet.GetLegacyScriptPubKeyMan();
+ if (spk_man && spk_man->HaveWatchOnly()) {
+ UniValue balances_watchonly{UniValue::VOBJ};
+ balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
+ balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
+ balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
+ balances.pushKV("watchonly", balances_watchonly);
+ }
+ return balances;
+},
+ };
+}
+
+RPCHelpMan listunspent()
+{
+ return RPCHelpMan{
+ "listunspent",
+ "\nReturns array of unspent transaction outputs\n"
+ "with between minconf and maxconf (inclusive) confirmations.\n"
+ "Optionally filter to only include txouts paid to specified addresses.\n",
+ {
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
+ {"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
+ {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The bitcoin addresses to filter",
+ {
+ {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address"},
+ },
+ },
+ {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include outputs that are not safe to spend\n"
+ "See description of \"safe\" attribute below."},
+ {"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
+ {
+ {"minimumAmount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)}, "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
+ {"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
+ },
+ "query_options"},
+ },
+ RPCResult{
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "the bitcoin address"},
+ {RPCResult::Type::STR, "label", /* optional */ true, "The associated label, or \"\" for the default label"},
+ {RPCResult::Type::STR, "scriptPubKey", "the script key"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::NUM, "ancestorcount", /* optional */ true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
+ {RPCResult::Type::NUM, "ancestorsize", /* optional */ true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
+ {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)"},
+ {RPCResult::Type::STR_HEX, "redeemScript", /* optional */ true, "The redeemScript if scriptPubKey is P2SH"},
+ {RPCResult::Type::STR, "witnessScript", /* optional */ true, "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
+ {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
+ {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
+ {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)"},
+ {RPCResult::Type::STR, "desc", /* optional */ true, "(only when solvable) A descriptor for spending this output"},
+ {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
+ "from outside keys and unconfirmed replacement transactions are considered unsafe\n"
+ "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
+ }},
+ }
+ },
+ RPCExamples{
+ HelpExampleCli("listunspent", "")
+ + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
+ + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
+ + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
+ + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
+
+ int nMinDepth = 1;
+ if (!request.params[0].isNull()) {
+ RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
+ nMinDepth = request.params[0].get_int();
+ }
+
+ int nMaxDepth = 9999999;
+ if (!request.params[1].isNull()) {
+ RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
+ nMaxDepth = request.params[1].get_int();
+ }
+
+ std::set<CTxDestination> destinations;
+ if (!request.params[2].isNull()) {
+ RPCTypeCheckArgument(request.params[2], UniValue::VARR);
+ UniValue inputs = request.params[2].get_array();
+ for (unsigned int idx = 0; idx < inputs.size(); idx++) {
+ const UniValue& input = inputs[idx];
+ CTxDestination dest = DecodeDestination(input.get_str());
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str());
+ }
+ if (!destinations.insert(dest).second) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
+ }
+ }
+ }
+
+ bool include_unsafe = true;
+ if (!request.params[3].isNull()) {
+ RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
+ include_unsafe = request.params[3].get_bool();
+ }
+
+ CAmount nMinimumAmount = 0;
+ CAmount nMaximumAmount = MAX_MONEY;
+ CAmount nMinimumSumAmount = MAX_MONEY;
+ uint64_t nMaximumCount = 0;
+
+ if (!request.params[4].isNull()) {
+ const UniValue& options = request.params[4].get_obj();
+
+ RPCTypeCheckObj(options,
+ {
+ {"minimumAmount", UniValueType()},
+ {"maximumAmount", UniValueType()},
+ {"minimumSumAmount", UniValueType()},
+ {"maximumCount", UniValueType(UniValue::VNUM)},
+ },
+ true, true);
+
+ if (options.exists("minimumAmount"))
+ nMinimumAmount = AmountFromValue(options["minimumAmount"]);
+
+ if (options.exists("maximumAmount"))
+ nMaximumAmount = AmountFromValue(options["maximumAmount"]);
+
+ if (options.exists("minimumSumAmount"))
+ nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
+
+ if (options.exists("maximumCount"))
+ nMaximumCount = options["maximumCount"].get_int64();
+ }
+
+ // Make sure the results are valid at least up to the most recent block
+ // the user could have gotten from another RPC command prior to now
+ pwallet->BlockUntilSyncedToCurrentChain();
+
+ UniValue results(UniValue::VARR);
+ std::vector<COutput> vecOutputs;
+ {
+ CCoinControl cctl;
+ cctl.m_avoid_address_reuse = false;
+ cctl.m_min_depth = nMinDepth;
+ cctl.m_max_depth = nMaxDepth;
+ cctl.m_include_unsafe_inputs = include_unsafe;
+ LOCK(pwallet->cs_wallet);
+ AvailableCoins(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+ }
+
+ LOCK(pwallet->cs_wallet);
+
+ const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
+
+ for (const COutput& out : vecOutputs) {
+ CTxDestination address;
+ const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
+ bool fValidAddress = ExtractDestination(scriptPubKey, address);
+ bool reused = avoid_reuse && pwallet->IsSpentKey(out.tx->GetHash(), out.i);
+
+ if (destinations.size() && (!fValidAddress || !destinations.count(address)))
+ continue;
+
+ UniValue entry(UniValue::VOBJ);
+ entry.pushKV("txid", out.tx->GetHash().GetHex());
+ entry.pushKV("vout", out.i);
+
+ if (fValidAddress) {
+ entry.pushKV("address", EncodeDestination(address));
+
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
+ if (address_book_entry) {
+ entry.pushKV("label", address_book_entry->GetLabel());
+ }
+
+ std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
+ if (provider) {
+ if (scriptPubKey.IsPayToScriptHash()) {
+ const CScriptID& hash = CScriptID(std::get<ScriptHash>(address));
+ CScript redeemScript;
+ if (provider->GetCScript(hash, redeemScript)) {
+ entry.pushKV("redeemScript", HexStr(redeemScript));
+ // Now check if the redeemScript is actually a P2WSH script
+ CTxDestination witness_destination;
+ if (redeemScript.IsPayToWitnessScriptHash()) {
+ bool extracted = ExtractDestination(redeemScript, witness_destination);
+ CHECK_NONFATAL(extracted);
+ // Also return the witness script
+ const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (provider->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript));
+ }
+ }
+ }
+ } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
+ const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(address);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (provider->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript));
+ }
+ }
+ }
+ }
+
+ entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
+ entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
+ entry.pushKV("confirmations", out.nDepth);
+ if (!out.nDepth) {
+ size_t ancestor_count, descendant_count, ancestor_size;
+ CAmount ancestor_fees;
+ pwallet->chain().getTransactionAncestry(out.tx->GetHash(), ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
+ if (ancestor_count) {
+ entry.pushKV("ancestorcount", uint64_t(ancestor_count));
+ entry.pushKV("ancestorsize", uint64_t(ancestor_size));
+ entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
+ }
+ }
+ entry.pushKV("spendable", out.fSpendable);
+ entry.pushKV("solvable", out.fSolvable);
+ if (out.fSolvable) {
+ std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
+ if (provider) {
+ auto descriptor = InferDescriptor(scriptPubKey, *provider);
+ entry.pushKV("desc", descriptor->ToString());
+ }
+ }
+ if (avoid_reuse) entry.pushKV("reused", reused);
+ entry.pushKV("safe", out.fSafe);
+ results.push_back(entry);
+ }
+
+ return results;
+},
+ };
+}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 1334e536c5..7ce0195568 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -19,7 +19,6 @@
#include <script/descriptor.h>
#include <script/sign.h>
#include <util/fees.h>
-#include <util/moneystr.h>
#include <util/string.h>
#include <util/system.h>
#include <util/translation.h>
@@ -255,228 +254,6 @@ static RPCHelpMan sendtoaddress()
};
}
-static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
-{
- std::set<CTxDestination> address_set;
-
- if (by_label) {
- // Get the set of addresses assigned to label
- std::string label = LabelFromValue(params[0]);
- address_set = wallet.GetLabelAddresses(label);
- } else {
- // Get the address
- CTxDestination dest = DecodeDestination(params[0].get_str());
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
- }
- CScript script_pub_key = GetScriptForDestination(dest);
- if (!wallet.IsMine(script_pub_key)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
- }
- address_set.insert(dest);
- }
-
- // Minimum confirmations
- int min_depth = 1;
- if (!params[1].isNull())
- min_depth = params[1].get_int();
-
- const bool include_immature_coinbase{params[2].isNull() ? false : params[2].get_bool()};
-
- // Excluding coinbase outputs is deprecated
- // It can be enabled by setting deprecatedrpc=exclude_coinbase
- const bool include_coinbase{!wallet.chain().rpcEnableDeprecated("exclude_coinbase")};
-
- if (include_immature_coinbase && !include_coinbase) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "include_immature_coinbase is incompatible with deprecated exclude_coinbase");
- }
-
- // Tally
- CAmount amount = 0;
- for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) {
- const CWalletTx& wtx = wtx_pair.second;
- int depth{wallet.GetTxDepthInMainChain(wtx)};
- if (depth < min_depth
- // Coinbase with less than 1 confirmation is no longer in the main chain
- || (wtx.IsCoinBase() && (depth < 1 || !include_coinbase))
- || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)
- || !wallet.chain().checkFinalTx(*wtx.tx)) {
- continue;
- }
-
- for (const CTxOut& txout : wtx.tx->vout) {
- CTxDestination address;
- if (ExtractDestination(txout.scriptPubKey, address) && wallet.IsMine(address) && address_set.count(address)) {
- amount += txout.nValue;
- }
- }
- }
-
- return amount;
-}
-
-
-static RPCHelpMan getreceivedbyaddress()
-{
- return RPCHelpMan{"getreceivedbyaddress",
- "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
- {
- {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
- {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
- {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
- },
- RPCResult{
- RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
- },
- RPCExamples{
- "\nThe amount from transactions with at least 1 confirmation\n"
- + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
- "\nThe amount including unconfirmed transactions, zero confirmations\n"
- + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0") +
- "\nThe amount with at least 6 confirmations\n"
- + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
- "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
- + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6 true") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
-
- // Make sure the results are valid at least up to the most recent block
- // the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
-
- LOCK(pwallet->cs_wallet);
-
- return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ false));
-},
- };
-}
-
-
-static RPCHelpMan getreceivedbylabel()
-{
- return RPCHelpMan{"getreceivedbylabel",
- "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
- {
- {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
- {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
- {"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
- },
- RPCResult{
- RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
- },
- RPCExamples{
- "\nAmount received by the default label with at least 1 confirmation\n"
- + HelpExampleCli("getreceivedbylabel", "\"\"") +
- "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
- + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
- "\nThe amount with at least 6 confirmations\n"
- + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
- "\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
- + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6 true") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6, true")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
-
- // Make sure the results are valid at least up to the most recent block
- // the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
-
- LOCK(pwallet->cs_wallet);
-
- return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ true));
-},
- };
-}
-
-
-static RPCHelpMan getbalance()
-{
- return RPCHelpMan{"getbalance",
- "\nReturns the total available balance.\n"
- "The available balance is what the wallet considers currently spendable, and is\n"
- "thus affected by options which limit spendability such as -spendzeroconfchange.\n",
- {
- {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
- {"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
- {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also include balance in watch-only addresses (see 'importaddress')"},
- {"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."},
- },
- RPCResult{
- RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
- },
- RPCExamples{
- "\nThe total amount in the wallet with 0 or more confirmations\n"
- + HelpExampleCli("getbalance", "") +
- "\nThe total amount in the wallet with at least 6 confirmations\n"
- + HelpExampleCli("getbalance", "\"*\" 6") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("getbalance", "\"*\", 6")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
-
- // Make sure the results are valid at least up to the most recent block
- // the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
-
- LOCK(pwallet->cs_wallet);
-
- const UniValue& dummy_value = request.params[0];
- if (!dummy_value.isNull() && dummy_value.get_str() != "*") {
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
- }
-
- int min_depth = 0;
- if (!request.params[1].isNull()) {
- min_depth = request.params[1].get_int();
- }
-
- bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
-
- bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
-
- const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
-
- return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
-},
- };
-}
-
-static RPCHelpMan getunconfirmedbalance()
-{
- return RPCHelpMan{"getunconfirmedbalance",
- "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
- {},
- RPCResult{RPCResult::Type::NUM, "", "The balance"},
- RPCExamples{""},
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
-
- // Make sure the results are valid at least up to the most recent block
- // the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
-
- LOCK(pwallet->cs_wallet);
-
- return ValueFromAmount(GetBalance(*pwallet).m_mine_untrusted_pending);
-},
- };
-}
-
-
static RPCHelpMan sendmany()
{
return RPCHelpMan{"sendmany",
@@ -570,199 +347,6 @@ static RPCHelpMan sendmany()
};
}
-static RPCHelpMan lockunspent()
-{
- return RPCHelpMan{"lockunspent",
- "\nUpdates list of temporarily unspendable outputs.\n"
- "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
- "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
- "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
- "Manually selected coins are automatically unlocked.\n"
- "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
- "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
- "(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
- "Also see the listunspent call\n",
- {
- {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
- {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
- {
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
- {
- {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
- {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
- },
- },
- },
- },
- {"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."},
- },
- RPCResult{
- RPCResult::Type::BOOL, "", "Whether the command was successful or not"
- },
- RPCExamples{
- "\nList the unspent transactions\n"
- + HelpExampleCli("listunspent", "") +
- "\nLock an unspent transaction\n"
- + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nList the locked transactions\n"
- + HelpExampleCli("listlockunspent", "") +
- "\nUnlock the transaction again\n"
- + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nLock the transaction persistently in the wallet database\n"
- + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
-
- // Make sure the results are valid at least up to the most recent block
- // the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
-
- LOCK(pwallet->cs_wallet);
-
- RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
-
- bool fUnlock = request.params[0].get_bool();
-
- const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
-
- if (request.params[1].isNull()) {
- if (fUnlock) {
- if (!pwallet->UnlockAllCoins())
- throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
- }
- return true;
- }
-
- RPCTypeCheckArgument(request.params[1], UniValue::VARR);
-
- const UniValue& output_params = request.params[1];
-
- // Create and validate the COutPoints first.
-
- std::vector<COutPoint> outputs;
- outputs.reserve(output_params.size());
-
- for (unsigned int idx = 0; idx < output_params.size(); idx++) {
- const UniValue& o = output_params[idx].get_obj();
-
- RPCTypeCheckObj(o,
- {
- {"txid", UniValueType(UniValue::VSTR)},
- {"vout", UniValueType(UniValue::VNUM)},
- });
-
- const uint256 txid(ParseHashO(o, "txid"));
- const int nOutput = find_value(o, "vout").get_int();
- if (nOutput < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
- }
-
- const COutPoint outpt(txid, nOutput);
-
- const auto it = pwallet->mapWallet.find(outpt.hash);
- if (it == pwallet->mapWallet.end()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
- }
-
- const CWalletTx& trans = it->second;
-
- if (outpt.n >= trans.tx->vout.size()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
- }
-
- if (pwallet->IsSpent(outpt.hash, outpt.n)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
- }
-
- const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n);
-
- if (fUnlock && !is_locked) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
- }
-
- if (!fUnlock && is_locked && !persistent) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
- }
-
- outputs.push_back(outpt);
- }
-
- std::unique_ptr<WalletBatch> batch = nullptr;
- // Unlock is always persistent
- if (fUnlock || persistent) batch = std::make_unique<WalletBatch>(pwallet->GetDatabase());
-
- // Atomically set (un)locked status for the outputs.
- for (const COutPoint& outpt : outputs) {
- if (fUnlock) {
- if (!pwallet->UnlockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
- } else {
- if (!pwallet->LockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
- }
- }
-
- return true;
-},
- };
-}
-
-static RPCHelpMan listlockunspent()
-{
- return RPCHelpMan{"listlockunspent",
- "\nReturns list of temporarily unspendable outputs.\n"
- "See the lockunspent call to lock and unlock transactions for spending.\n",
- {},
- RPCResult{
- RPCResult::Type::ARR, "", "",
- {
- {RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
- {RPCResult::Type::NUM, "vout", "The vout value"},
- }},
- }
- },
- RPCExamples{
- "\nList the unspent transactions\n"
- + HelpExampleCli("listunspent", "") +
- "\nLock an unspent transaction\n"
- + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nList the locked transactions\n"
- + HelpExampleCli("listlockunspent", "") +
- "\nUnlock the transaction again\n"
- + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nAs a JSON-RPC call\n"
- + HelpExampleRpc("listlockunspent", "")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
-
- LOCK(pwallet->cs_wallet);
-
- std::vector<COutPoint> vOutpts;
- pwallet->ListLockedCoins(vOutpts);
-
- UniValue ret(UniValue::VARR);
-
- for (const COutPoint& outpt : vOutpts) {
- UniValue o(UniValue::VOBJ);
-
- o.pushKV("txid", outpt.hash.GetHex());
- o.pushKV("vout", (int)outpt.n);
- ret.push_back(o);
- }
-
- return ret;
-},
- };
-}
-
static RPCHelpMan settxfee()
{
return RPCHelpMan{"settxfee",
@@ -804,73 +388,6 @@ static RPCHelpMan settxfee()
};
}
-static RPCHelpMan getbalances()
-{
- return RPCHelpMan{
- "getbalances",
- "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
- {},
- RPCResult{
- RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
- {
- {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
- {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
- {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
- {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)"},
- }},
- {RPCResult::Type::OBJ, "watchonly", /* optional */ true, "watchonly balances (not present if wallet does not watch anything)",
- {
- {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
- {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
- {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
- }},
- }
- },
- RPCExamples{
- HelpExampleCli("getbalances", "") +
- HelpExampleRpc("getbalances", "")},
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
- if (!rpc_wallet) return NullUniValue;
- const CWallet& wallet = *rpc_wallet;
-
- // Make sure the results are valid at least up to the most recent block
- // the user could have gotten from another RPC command prior to now
- wallet.BlockUntilSyncedToCurrentChain();
-
- LOCK(wallet.cs_wallet);
-
- const auto bal = GetBalance(wallet);
- UniValue balances{UniValue::VOBJ};
- {
- UniValue balances_mine{UniValue::VOBJ};
- balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
- balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
- balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
- if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
- // If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
- // the total balance, and then subtract bal to get the reused address balance.
- const auto full_bal = GetBalance(wallet, 0, false);
- 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));
- }
- balances.pushKV("mine", balances_mine);
- }
- auto spk_man = wallet.GetLegacyScriptPubKeyMan();
- if (spk_man && spk_man->HaveWatchOnly()) {
- UniValue balances_watchonly{UniValue::VOBJ};
- balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
- balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
- balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
- balances.pushKV("watchonly", balances_watchonly);
- }
- return balances;
-},
- };
-}
-
static RPCHelpMan getwalletinfo()
{
return RPCHelpMan{"getwalletinfo",
@@ -1284,242 +801,6 @@ static RPCHelpMan unloadwallet()
};
}
-static RPCHelpMan listunspent()
-{
- return RPCHelpMan{
- "listunspent",
- "\nReturns array of unspent transaction outputs\n"
- "with between minconf and maxconf (inclusive) confirmations.\n"
- "Optionally filter to only include txouts paid to specified addresses.\n",
- {
- {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
- {"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
- {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The bitcoin addresses to filter",
- {
- {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address"},
- },
- },
- {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include outputs that are not safe to spend\n"
- "See description of \"safe\" attribute below."},
- {"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
- {
- {"minimumAmount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)}, "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
- {"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
- {"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
- {"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
- },
- "query_options"},
- },
- RPCResult{
- RPCResult::Type::ARR, "", "",
- {
- {RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
- {RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR, "address", /* optional */ true, "the bitcoin address"},
- {RPCResult::Type::STR, "label", /* optional */ true, "The associated label, or \"\" for the default label"},
- {RPCResult::Type::STR, "scriptPubKey", "the script key"},
- {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
- {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
- {RPCResult::Type::NUM, "ancestorcount", /* optional */ true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
- {RPCResult::Type::NUM, "ancestorsize", /* optional */ true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
- {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)"},
- {RPCResult::Type::STR_HEX, "redeemScript", /* optional */ true, "The redeemScript if scriptPubKey is P2SH"},
- {RPCResult::Type::STR, "witnessScript", /* optional */ true, "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
- {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
- {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
- {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)"},
- {RPCResult::Type::STR, "desc", /* optional */ true, "(only when solvable) A descriptor for spending this output"},
- {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
- "from outside keys and unconfirmed replacement transactions are considered unsafe\n"
- "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
- }},
- }
- },
- RPCExamples{
- HelpExampleCli("listunspent", "")
- + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
- + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
- + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
- + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
- },
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
-
- int nMinDepth = 1;
- if (!request.params[0].isNull()) {
- RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- nMinDepth = request.params[0].get_int();
- }
-
- int nMaxDepth = 9999999;
- if (!request.params[1].isNull()) {
- RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
- nMaxDepth = request.params[1].get_int();
- }
-
- std::set<CTxDestination> destinations;
- if (!request.params[2].isNull()) {
- RPCTypeCheckArgument(request.params[2], UniValue::VARR);
- UniValue inputs = request.params[2].get_array();
- for (unsigned int idx = 0; idx < inputs.size(); idx++) {
- const UniValue& input = inputs[idx];
- CTxDestination dest = DecodeDestination(input.get_str());
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str());
- }
- if (!destinations.insert(dest).second) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
- }
- }
- }
-
- bool include_unsafe = true;
- if (!request.params[3].isNull()) {
- RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
- include_unsafe = request.params[3].get_bool();
- }
-
- CAmount nMinimumAmount = 0;
- CAmount nMaximumAmount = MAX_MONEY;
- CAmount nMinimumSumAmount = MAX_MONEY;
- uint64_t nMaximumCount = 0;
-
- if (!request.params[4].isNull()) {
- const UniValue& options = request.params[4].get_obj();
-
- RPCTypeCheckObj(options,
- {
- {"minimumAmount", UniValueType()},
- {"maximumAmount", UniValueType()},
- {"minimumSumAmount", UniValueType()},
- {"maximumCount", UniValueType(UniValue::VNUM)},
- },
- true, true);
-
- if (options.exists("minimumAmount"))
- nMinimumAmount = AmountFromValue(options["minimumAmount"]);
-
- if (options.exists("maximumAmount"))
- nMaximumAmount = AmountFromValue(options["maximumAmount"]);
-
- if (options.exists("minimumSumAmount"))
- nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
-
- if (options.exists("maximumCount"))
- nMaximumCount = options["maximumCount"].get_int64();
- }
-
- // Make sure the results are valid at least up to the most recent block
- // the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
-
- UniValue results(UniValue::VARR);
- std::vector<COutput> vecOutputs;
- {
- CCoinControl cctl;
- cctl.m_avoid_address_reuse = false;
- cctl.m_min_depth = nMinDepth;
- cctl.m_max_depth = nMaxDepth;
- cctl.m_include_unsafe_inputs = include_unsafe;
- LOCK(pwallet->cs_wallet);
- AvailableCoins(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
- }
-
- LOCK(pwallet->cs_wallet);
-
- const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
-
- for (const COutput& out : vecOutputs) {
- CTxDestination address;
- const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
- bool fValidAddress = ExtractDestination(scriptPubKey, address);
- bool reused = avoid_reuse && pwallet->IsSpentKey(out.tx->GetHash(), out.i);
-
- if (destinations.size() && (!fValidAddress || !destinations.count(address)))
- continue;
-
- UniValue entry(UniValue::VOBJ);
- entry.pushKV("txid", out.tx->GetHash().GetHex());
- entry.pushKV("vout", out.i);
-
- if (fValidAddress) {
- entry.pushKV("address", EncodeDestination(address));
-
- const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
- if (address_book_entry) {
- entry.pushKV("label", address_book_entry->GetLabel());
- }
-
- std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
- if (provider) {
- if (scriptPubKey.IsPayToScriptHash()) {
- const CScriptID& hash = CScriptID(std::get<ScriptHash>(address));
- CScript redeemScript;
- if (provider->GetCScript(hash, redeemScript)) {
- entry.pushKV("redeemScript", HexStr(redeemScript));
- // Now check if the redeemScript is actually a P2WSH script
- CTxDestination witness_destination;
- if (redeemScript.IsPayToWitnessScriptHash()) {
- bool extracted = ExtractDestination(redeemScript, witness_destination);
- CHECK_NONFATAL(extracted);
- // Also return the witness script
- const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination);
- CScriptID id;
- CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
- CScript witnessScript;
- if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript));
- }
- }
- }
- } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
- const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(address);
- CScriptID id;
- CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
- CScript witnessScript;
- if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript));
- }
- }
- }
- }
-
- entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
- entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
- entry.pushKV("confirmations", out.nDepth);
- if (!out.nDepth) {
- size_t ancestor_count, descendant_count, ancestor_size;
- CAmount ancestor_fees;
- pwallet->chain().getTransactionAncestry(out.tx->GetHash(), ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
- if (ancestor_count) {
- entry.pushKV("ancestorcount", uint64_t(ancestor_count));
- entry.pushKV("ancestorsize", uint64_t(ancestor_size));
- entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
- }
- }
- entry.pushKV("spendable", out.fSpendable);
- entry.pushKV("solvable", out.fSolvable);
- if (out.fSolvable) {
- std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
- if (provider) {
- auto descriptor = InferDescriptor(scriptPubKey, *provider);
- entry.pushKV("desc", descriptor->ToString());
- }
- }
- if (avoid_reuse) entry.pushKV("reused", reused);
- entry.pushKV("safe", out.fSafe);
- results.push_back(entry);
- }
-
- return results;
-},
- };
-}
-
// Only includes key documentation where the key is snake_case in all RPC methods. MixedCase keys can be added later.
static std::vector<RPCArg> FundTxDoc()
{
@@ -2696,6 +1977,16 @@ RPCHelpMan listlabels();
RPCHelpMan walletdisplayaddress();
#endif // ENABLE_EXTERNAL_SIGNER
+// coins
+RPCHelpMan getreceivedbyaddress();
+RPCHelpMan getreceivedbylabel();
+RPCHelpMan getbalance();
+RPCHelpMan getunconfirmedbalance();
+RPCHelpMan lockunspent();
+RPCHelpMan listlockunspent();
+RPCHelpMan getbalances();
+RPCHelpMan listunspent();
+
// encryption
RPCHelpMan walletpassphrase();
RPCHelpMan walletpassphrasechange();