Skip to content

Commit 1bc8106

Browse files
committed
bumpfee: be able to bump fee of a tx with external inputs
In some cases, notably psbtbumpfee, it is okay, and potentially desired, to be able to bump the fee of a transaction which contains external inputs.
1 parent 31dd3dc commit 1bc8106

File tree

4 files changed

+28
-16
lines changed

4 files changed

+28
-16
lines changed

src/wallet/feebumper.cpp

+13-12
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
namespace wallet {
2121
//! Check whether transaction has descendant in wallet or mempool, or has been
2222
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
23-
static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
23+
static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, bool require_mine, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
2424
{
2525
if (wallet.HasWalletSpend(wtx.tx)) {
2626
errors.push_back(Untranslated("Transaction has descendants in the wallet"));
@@ -49,15 +49,16 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
4949
return feebumper::Result::WALLET_ERROR;
5050
}
5151

52-
// check that original tx consists entirely of our inputs
53-
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
54-
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
55-
if (!AllInputsMine(wallet, *wtx.tx, filter)) {
56-
errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
57-
return feebumper::Result::WALLET_ERROR;
52+
if (require_mine) {
53+
// check that original tx consists entirely of our inputs
54+
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
55+
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
56+
if (!AllInputsMine(wallet, *wtx.tx, filter)) {
57+
errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
58+
return feebumper::Result::WALLET_ERROR;
59+
}
5860
}
5961

60-
6162
return feebumper::Result::OK;
6263
}
6364

@@ -149,12 +150,12 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
149150
if (wtx == nullptr) return false;
150151

151152
std::vector<bilingual_str> errors_dummy;
152-
feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
153+
feebumper::Result res = PreconditionChecks(wallet, *wtx, /* require_mine=*/ true, errors_dummy);
153154
return res == feebumper::Result::OK;
154155
}
155156

156157
Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
157-
CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
158+
CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine)
158159
{
159160
// We are going to modify coin control later, copy to re-use
160161
CCoinControl new_coin_control(coin_control);
@@ -216,7 +217,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
216217
}
217218
}
218219

219-
Result result = PreconditionChecks(wallet, wtx, errors);
220+
Result result = PreconditionChecks(wallet, wtx, require_mine, errors);
220221
if (result != Result::OK) {
221222
return result;
222223
}
@@ -309,7 +310,7 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti
309310
const CWalletTx& oldWtx = it->second;
310311

311312
// make sure the transaction still has no descendants and hasn't been mined in the meantime
312-
Result result = PreconditionChecks(wallet, oldWtx, errors);
313+
Result result = PreconditionChecks(wallet, oldWtx, /* require_mine=*/ false, errors);
313314
if (result != Result::OK) {
314315
return result;
315316
}

src/wallet/feebumper.h

+13-2
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,25 @@ enum class Result
3333
//! Return whether transaction can be bumped.
3434
bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid);
3535

36-
//! Create bumpfee transaction based on feerate estimates.
36+
/** Create bumpfee transaction based on feerate estimates.
37+
*
38+
* @param[in] wallet The wallet to use for this bumping
39+
* @param[in] txid The txid of the transaction to bump
40+
* @param[in] coin_control A CCoinControl object which provides feerates and other information used for coin selection
41+
* @param[out] errors Errors
42+
* @param[out] old_fee The fee the original transaction pays
43+
* @param[out] new_fee the fee that the bump transaction pays
44+
* @param[out] mtx The bump transaction itself
45+
* @param[in] require_mine Whether the original transaction must consist of inputs that can be spent by the wallet
46+
*/
3747
Result CreateRateBumpTransaction(CWallet& wallet,
3848
const uint256& txid,
3949
const CCoinControl& coin_control,
4050
std::vector<bilingual_str>& errors,
4151
CAmount& old_fee,
4252
CAmount& new_fee,
43-
CMutableTransaction& mtx);
53+
CMutableTransaction& mtx,
54+
bool require_mine);
4455

4556
//! Sign the new transaction,
4657
//! @return false if the tx couldn't be found or if it was

src/wallet/interfaces.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class WalletImpl : public Wallet
291291
CAmount& new_fee,
292292
CMutableTransaction& mtx) override
293293
{
294-
return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx) == feebumper::Result::OK;
294+
return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx, /* require_mine= */ true) == feebumper::Result::OK;
295295
}
296296
bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(*m_wallet.get(), mtx); }
297297
bool commitBumpTransaction(const uint256& txid,

src/wallet/rpc/spend.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
10451045
CMutableTransaction mtx;
10461046
feebumper::Result res;
10471047
// Targeting feerate bump.
1048-
res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
1048+
res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt);
10491049
if (res != feebumper::Result::OK) {
10501050
switch(res) {
10511051
case feebumper::Result::INVALID_ADDRESS_OR_KEY:

0 commit comments

Comments
 (0)