From eda384078d157f090ef67bd422a7d3f8ffc84e71 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sat, 14 Nov 2020 15:38:40 +0100 Subject: [PATCH] wallet: introduce setfeerate, an improved settxfee in sat/vB Github-Pull: #20391 Rebased-From: 8e863e3d3ce457c9ca26a12e8fd6beac2f50aa7d (diff-minimised) --- src/rpc/client.cpp | 1 + src/wallet/rpc/spend.cpp | 74 +++++++++++++++++++++++++++++++++++++++ src/wallet/rpc/wallet.cpp | 2 ++ 3 files changed, 77 insertions(+) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 5825efdf82..91efe184af 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -47,6 +47,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendtoaddress", 8, "avoid_reuse" }, { "sendtoaddress", 9, "fee_rate"}, { "sendtoaddress", 10, "verbose"}, + { "setfeerate", 0, "amount" }, { "settxfee", 0, "amount" }, { "sethdseed", 0, "newkeypool" }, { "getreceivedbyaddress", 1, "minconf" }, diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index 6060f017ce..5b809a7c41 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -408,6 +408,80 @@ RPCHelpMan sendmany() }; } +RPCHelpMan setfeerate() +{ + return RPCHelpMan{ + "setfeerate", + "\nSet the transaction fee rate in " + CURRENCY_ATOM + "/vB for this wallet.\n" + "Overrides the global -paytxfee configuration option. Like -paytxfee, it is not persisted after bitcoind shutdown/restart.\n" + "Can be deactivated by passing 0 as the fee rate, in which case automatic fee selection will be used by default.\n", + { + {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee rate in " + CURRENCY_ATOM + "/vB to set (0 to unset)"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "wallet_name", "Name of the wallet the fee rate setting applies to"}, + {RPCResult::Type::NUM, "fee_rate", "Fee rate in " + CURRENCY_ATOM + "/vB for the wallet after this operation"}, + {RPCResult::Type::STR, "result", /* optional */ true, "Description of result, if successful"}, + {RPCResult::Type::STR, "error", /* optional */ true, "Description of error, if any"}, + }, + }, + RPCExamples{ + "" + "\nSet a fee rate of 1 " + CURRENCY_ATOM + "/vB\n" + + HelpExampleCli("setfeerate", "1") + + "\nSet a fee rate of 3.141 " + CURRENCY_ATOM + "/vB\n" + + HelpExampleCli("setfeerate", "3.141") + + "\nSet a fee rate of 7.75 " + CURRENCY_ATOM + "/vB with named arguments\n" + + HelpExampleCli("-named setfeerate", "amount=\"7.75\"") + + "\nSet a fee rate of 25 " + CURRENCY_ATOM + "/vB with the RPC\n" + + HelpExampleRpc("setfeerate", "25") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + std::shared_ptr const rpc_wallet{GetWalletForJSONRPCRequest(request)}; + if (!rpc_wallet) return NullUniValue; + CWallet& wallet = *rpc_wallet; + + LOCK(wallet.cs_wallet); + const CFeeRate amount{AmountFromValue(request.params[0]), COIN /* sat/vB */}; + const CFeeRate relay_min_feerate{wallet.chain().relayMinFee().GetFeePerK()}; + const CFeeRate wallet_min_feerate{wallet.m_min_fee.GetFeePerK()}; + const CFeeRate wallet_max_feerate{wallet.m_default_max_tx_fee, 1000 /* BTC/kvB */}; + const CFeeRate zero{CFeeRate{0}}; + const std::string amount_str{amount.ToString(FeeEstimateMode::SAT_VB)}; + const std::string current_setting{strprintf("The current setting of %s for this wallet remains unchanged.", wallet.m_pay_tx_fee == zero ? "0 (unset)" : wallet.m_pay_tx_fee.ToString(FeeEstimateMode::SAT_VB))}; + std::string result, error; + + if (amount == zero) { + if (request.params[0].get_real() != 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + wallet.m_pay_tx_fee = amount; + result = "Fee rate for transactions with this wallet successfully unset. By default, automatic fee selection will be used."; + } else if (amount < relay_min_feerate) { + error = strprintf("The requested fee rate of %s cannot be less than the minimum relay fee rate of %s. %s", amount_str, relay_min_feerate.ToString(FeeEstimateMode::SAT_VB), current_setting); + } else if (amount < wallet_min_feerate) { + error = strprintf("The requested fee rate of %s cannot be less than the wallet min fee rate of %s. %s", amount_str, wallet_min_feerate.ToString(FeeEstimateMode::SAT_VB), current_setting); + } else if (amount > wallet_max_feerate) { + error = strprintf("The requested fee rate of %s cannot be greater than the wallet max fee rate of %s. %s", amount_str, wallet_max_feerate.ToString(FeeEstimateMode::SAT_VB), current_setting); + } else { + wallet.m_pay_tx_fee = amount; + result = "Fee rate for transactions with this wallet successfully set to " + amount_str; + } + CHECK_NONFATAL(result.empty() != error.empty()); + + UniValue obj{UniValue::VOBJ}; + obj.pushKV("wallet_name", wallet.GetName()); + obj.pushKV("fee_rate", ValueFromFeeRate(wallet.m_pay_tx_fee)); + if (error.empty()) { + obj.pushKV("result", result); + } else { + obj.pushKV("error", error); + } + return obj; + }, + }; +} + RPCHelpMan settxfee() { return RPCHelpMan{"settxfee", diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index 6a8ce954fb..36b86769f3 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -866,6 +866,7 @@ RPCHelpMan encryptwallet(); // spend RPCHelpMan sendtoaddress(); RPCHelpMan sendmany(); +RPCHelpMan setfeerate(); RPCHelpMan settxfee(); RPCHelpMan fundrawtransaction(); RPCHelpMan bumpfee(); @@ -945,6 +946,7 @@ Span GetWalletRPCCommands() {"wallet", &sendtoaddress}, {"wallet", &sethdseed}, {"wallet", &setlabel}, + {"wallet", &setfeerate}, {"wallet", &settxfee}, {"wallet", &setwalletflag}, {"wallet", &signmessage},