diff options
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r-- | src/wallet/rpcwallet.cpp | 139 |
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, {} }, |