aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/rpcwallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r--src/wallet/rpcwallet.cpp139
1 files changed, 50 insertions, 89 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index d32613fc77..7de74bac4d 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -7,7 +7,6 @@
#include <chain.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <httpserver.h>
#include <init.h>
#include <interfaces/chain.h>
#include <validation.h>
@@ -19,7 +18,7 @@
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
-#include <rpc/rawtransaction.h>
+#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
@@ -27,8 +26,11 @@
#include <shutdown.h>
#include <timedata.h>
#include <util/bip32.h>
+#include <util/fees.h>
#include <util/system.h>
#include <util/moneystr.h>
+#include <util/url.h>
+#include <util/validation.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/psbtwallet.h>
@@ -308,7 +310,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{
- CAmount curBalance = pwallet->GetBalance();
+ CAmount curBalance = pwallet->GetBalance().m_mine_trusted;
// Check amount
if (nValue <= 0)
@@ -761,12 +763,14 @@ static UniValue getbalance(const JSONRPCRequest& request)
min_depth = request.params[1].get_int();
}
- isminefilter filter = ISMINE_SPENDABLE;
+ bool include_watchonly = false;
if (!request.params[2].isNull() && request.params[2].get_bool()) {
- filter = filter | ISMINE_WATCH_ONLY;
+ include_watchonly = true;
}
- return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
+ const auto bal = pwallet->GetBalance(min_depth);
+
+ return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
}
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -794,7 +798,7 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- return ValueFromAmount(pwallet->GetUnconfirmedBalance());
+ return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
}
@@ -807,9 +811,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
- throw std::runtime_error(
- RPCHelpMan{"sendmany",
+ const RPCHelpMan help{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HelpRequiringPassphrase(pwallet) + "\n",
{
@@ -819,7 +821,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
},
},
- {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only use the balance confirmed at least this many times."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
{"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array with addresses.\n"
" The fee will be equally deducted from the amount of each selected address.\n"
@@ -850,7 +852,11 @@ static UniValue sendmany(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
// 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
@@ -867,9 +873,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
}
UniValue sendTo = request.params[1].get_obj();
- int nMinDepth = 1;
- if (!request.params[2].isNull())
- nMinDepth = request.params[2].get_int();
mapValue_t mapValue;
if (!request.params[3].isNull() && !request.params[3].get_str().empty())
@@ -897,7 +900,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend;
- CAmount totalAmount = 0;
std::vector<std::string> keys = sendTo.getKeys();
for (const std::string& name_ : keys) {
CTxDestination dest = DecodeDestination(name_);
@@ -914,7 +916,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
CAmount nAmount = AmountFromValue(sendTo[name_]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
- totalAmount += nAmount;
bool fSubtractFeeFromAmount = false;
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
@@ -929,11 +930,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- // Check funds
- if (totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth)) {
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds");
- }
-
// Shuffle recipient list
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
@@ -1760,7 +1756,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
- std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
+ std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
entry.pushKV("hex", strHex);
return entry;
@@ -1978,7 +1974,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
// wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded.
std::weak_ptr<CWallet> weak_wallet = wallet;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
+ pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
if (auto shared_wallet = weak_wallet.lock()) {
LOCK(shared_wallet->cs_wallet);
shared_wallet->Lock();
@@ -2424,11 +2420,12 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
+ const auto bal = pwallet->GetBalance();
obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion());
- obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance()));
- obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()));
- obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()));
+ obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
+ obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
+ obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
@@ -2668,50 +2665,6 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
return NullUniValue;
}
-static UniValue resendwallettransactions(const JSONRPCRequest& request)
-{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
- RPCHelpMan{"resendwallettransactions",
- "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
- "Intended only for testing; the wallet code periodically re-broadcasts\n"
- "automatically.\n",
- {},
- RPCResult{
- "Returns an RPC error if -walletbroadcast is set to false.\n"
- "Returns array of transaction ids that were re-broadcast.\n"
- },
- RPCExamples{""},
- }.ToString()
- );
-
- if (!pwallet->chain().p2pEnabled()) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
-
- auto locked_chain = pwallet->chain().lock();
- LOCK(pwallet->cs_wallet);
-
- if (!pwallet->GetBroadcastTransactions()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast");
- }
-
- std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime());
- UniValue result(UniValue::VARR);
- for (const uint256& txid : txids)
- {
- result.push_back(txid.ToString());
- }
- return result;
-}
-
static UniValue listunspent(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -3150,7 +3103,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
- {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "(required for Segwit inputs) the amount spent"},
},
},
},
@@ -3214,9 +3167,9 @@ static UniValue bumpfee(const JSONRPCRequest& request)
RPCHelpMan{"bumpfee",
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
- "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
- "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
- "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
+ "The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
+ "If `totalFee` is given, adding inputs is not supported, so there must be a single change output that is big enough or it will fail.\n"
+ "All inputs in the original transaction will be included in the replacement transaction.\n"
"The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
"By default, the new fee will be calculated automatically using estimatesmartfee.\n"
"The user can specify a confirmation target for estimatesmartfee.\n"
@@ -3313,7 +3266,14 @@ static UniValue bumpfee(const JSONRPCRequest& request)
CAmount old_fee;
CAmount new_fee;
CMutableTransaction mtx;
- feebumper::Result res = feebumper::CreateTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx);
+ feebumper::Result res;
+ if (totalFee > 0) {
+ // Targeting total fee bump. Requires a change output of sufficient size.
+ res = feebumper::CreateTotalBumpTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx);
+ } else {
+ // Targeting feerate bump.
+ res = feebumper::CreateRateBumpTransaction(pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
+ }
if (res != feebumper::Result::OK) {
switch(res) {
case feebumper::Result::INVALID_ADDRESS_OR_KEY:
@@ -3455,7 +3415,7 @@ class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
public:
CWallet * const pwallet;
- void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const
+ void ProcessSubScript(const CScript& subscript, UniValue& obj) const
{
// Always present: script type and redeemscript
std::vector<std::vector<unsigned char>> solutions_data;
@@ -3464,7 +3424,6 @@ public:
obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
CTxDestination embedded;
- UniValue a(UniValue::VARR);
if (ExtractDestination(subscript, embedded)) {
// Only when the script corresponds to an address.
UniValue subobj(UniValue::VOBJ);
@@ -3477,7 +3436,6 @@ public:
// Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
obj.pushKV("embedded", std::move(subobj));
- if (include_addresses) a.push_back(EncodeDestination(embedded));
} else if (which_type == TX_MULTISIG) {
// Also report some information on multisig scripts (which do not have a corresponding address).
// TODO: abstract out the common functionality between this logic and ExtractDestinations.
@@ -3485,17 +3443,10 @@ public:
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
- if (include_addresses) a.push_back(EncodeDestination(key.GetID()));
pubkeys.push_back(HexStr(key.begin(), key.end()));
}
obj.pushKV("pubkeys", std::move(pubkeys));
}
-
- // The "addresses" field is confusing because it refers to public keys using their P2PKH address.
- // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications
- // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for
- // inspecting multisig participants.
- if (include_addresses) obj.pushKV("addresses", std::move(a));
}
explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {}
@@ -3518,7 +3469,7 @@ public:
UniValue obj(UniValue::VOBJ);
CScript subscript;
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- ProcessSubScript(subscript, obj, IsDeprecatedRPCEnabled("validateaddress"));
+ ProcessSubScript(subscript, obj);
}
return obj;
}
@@ -3732,9 +3683,20 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
// Find all addresses that have the given label
UniValue ret(UniValue::VOBJ);
+ std::set<std::string> addresses;
for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
if (item.second.name == label) {
- ret.pushKV(EncodeDestination(item.first), AddressBookDataToJSON(item.second, false));
+ std::string address = EncodeDestination(item.first);
+ // CWallet::mapAddressBook is not expected to contain duplicate
+ // address strings, but build a separate set as a precaution just in
+ // case it does.
+ bool unique = addresses.emplace(address).second;
+ assert(unique);
+ // UniValue::pushKV checks if the key exists in O(N)
+ // and since duplicate addresses are unexpected (checked with
+ // std::set in O(log(N))), UniValue::__pushKV is used instead,
+ // which currently is O(1).
+ ret.__pushKV(address, AddressBookDataToJSON(item.second, false));
}
}
@@ -4085,7 +4047,6 @@ UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
- { "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },