aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/wallet/feebumper.cpp25
-rw-r--r--src/wallet/feebumper.h5
-rw-r--r--src/wallet/rpc/spend.cpp13
4 files changed, 36 insertions, 9 deletions
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 5f58eef1db..d289a9240e 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -259,11 +259,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "bumpfee", 1, "fee_rate"},
{ "bumpfee", 1, "replaceable"},
{ "bumpfee", 1, "outputs"},
+ { "bumpfee", 1, "reduce_output"},
{ "psbtbumpfee", 1, "options" },
{ "psbtbumpfee", 1, "conf_target"},
{ "psbtbumpfee", 1, "fee_rate"},
{ "psbtbumpfee", 1, "replaceable"},
{ "psbtbumpfee", 1, "outputs"},
+ { "psbtbumpfee", 1, "reduce_output"},
{ "logging", 0, "include" },
{ "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" },
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 0e1f1f9847..3720d144eb 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -152,8 +152,14 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
}
Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
- CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, const std::vector<CTxOut>& outputs)
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, const std::vector<CTxOut>& outputs, std::optional<uint32_t> reduce_output)
{
+ // Cannot both specify new outputs and an output to reduce
+ if (!outputs.empty() && reduce_output.has_value()) {
+ errors.push_back(Untranslated("Cannot specify both new outputs to use and an output index to reduce"));
+ return Result::INVALID_PARAMETER;
+ }
+
// We are going to modify coin control later, copy to re-use
CCoinControl new_coin_control(coin_control);
@@ -166,6 +172,12 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
}
const CWalletTx& wtx = it->second;
+ // Make sure that reduce_output is valid
+ if (reduce_output.has_value() && reduce_output.value() >= wtx.tx->vout.size()) {
+ errors.push_back(Untranslated("Change position is out of range"));
+ return Result::INVALID_PARAMETER;
+ }
+
// Retrieve all of the UTXOs and add them to coin control
// While we're here, calculate the input amount
std::map<COutPoint, Coin> coins;
@@ -233,14 +245,15 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
std::vector<CRecipient> recipients;
CAmount new_outputs_value = 0;
const auto& txouts = outputs.empty() ? wtx.tx->vout : outputs;
- for (const auto& output : txouts) {
- if (!OutputIsChange(wallet, output)) {
- CRecipient recipient = {output.scriptPubKey, output.nValue, false};
- recipients.push_back(recipient);
- } else {
+ for (size_t i = 0; i < txouts.size(); ++i) {
+ const CTxOut& output = txouts.at(i);
+ if (reduce_output.has_value() ? reduce_output.value() == i : OutputIsChange(wallet, output)) {
CTxDestination change_dest;
ExtractDestination(output.scriptPubKey, change_dest);
new_coin_control.destChange = change_dest;
+ } else {
+ CRecipient recipient = {output.scriptPubKey, output.nValue, false};
+ recipients.push_back(recipient);
}
new_outputs_value += output.nValue;
}
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index 53cf16e0f1..f00bf15730 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -43,6 +43,8 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid);
* @param[out] new_fee the fee that the bump transaction pays
* @param[out] mtx The bump transaction itself
* @param[in] require_mine Whether the original transaction must consist of inputs that can be spent by the wallet
+ * @param[in] outputs Vector of new outputs to replace the bumped transaction's outputs
+ * @param[in] reduce_output The position of the change output to deduct the fee from in the transaction being bumped
*/
Result CreateRateBumpTransaction(CWallet& wallet,
const uint256& txid,
@@ -52,7 +54,8 @@ Result CreateRateBumpTransaction(CWallet& wallet,
CAmount& new_fee,
CMutableTransaction& mtx,
bool require_mine,
- const std::vector<CTxOut>& outputs);
+ const std::vector<CTxOut>& outputs,
+ std::optional<uint32_t> reduce_output = std::nullopt);
//! Sign the new transaction,
//! @return false if the tx couldn't be found or if it was
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index feee643f26..b695d4bed3 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -1015,9 +1015,11 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
"\"" + FeeModes("\"\n\"") + "\""},
{"outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "New outputs (key-value pairs) which will replace\n"
"the original ones, if provided. Each address can only appear once and there can\n"
- "only be one \"data\" object.\n",
+ "only be one \"data\" object.\n"
+ "Cannot be provided if 'reduce_output' is specified.",
OutputsDoc(),
RPCArgOptions{.skip_type_check = true}},
+ {"reduce_output", RPCArg::Type::NUM, RPCArg::DefaultHint{"not set, detect change automatically"}, "The 0-based index of the output from which the additional fees will be deducted. In general, this should be the position of change output. Cannot be provided if 'outputs' is specified."},
},
RPCArgOptions{.oneline_description="options"}},
},
@@ -1056,6 +1058,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
coin_control.m_signal_bip125_rbf = true;
std::vector<CTxOut> outputs;
+ std::optional<uint32_t> reduce_output;
+
if (!request.params[1].isNull()) {
UniValue options = request.params[1];
RPCTypeCheckObj(options,
@@ -1066,6 +1070,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
{"replaceable", UniValueType(UniValue::VBOOL)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
{"outputs", UniValueType()}, // will be checked by AddOutputs()
+ {"reduce_output", UniValueType(UniValue::VNUM)},
},
true, true);
@@ -1089,6 +1094,10 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
AddOutputs(tempTx, options["outputs"]);
outputs = tempTx.vout;
}
+
+ if (options.exists("reduce_output")) {
+ reduce_output = options["reduce_output"].getInt<uint32_t>();
+ }
}
// Make sure the results are valid at least up to the most recent block
@@ -1106,7 +1115,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
CMutableTransaction mtx;
feebumper::Result res;
// Targeting feerate bump.
- res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt, outputs);
+ res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt, outputs, reduce_output);
if (res != feebumper::Result::OK) {
switch(res) {
case feebumper::Result::INVALID_ADDRESS_OR_KEY: