@@ -75,9 +75,11 @@ bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid)
75
75
return res == feebumper::Result::OK;
76
76
}
77
77
78
- Result CreateTransaction (const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
79
- CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
78
+ Result CreateTotalBumpTransaction (const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
79
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
80
80
{
81
+ new_fee = total_fee;
82
+
81
83
auto locked_chain = wallet->chain ().lock ();
82
84
LOCK (wallet->cs_wallet );
83
85
errors.clear ();
@@ -121,7 +123,6 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
121
123
// calculate the old fee and fee-rate
122
124
old_fee = wtx.GetDebit (ISMINE_SPENDABLE) - wtx.tx ->GetValueOut ();
123
125
CFeeRate nOldFeeRate (old_fee, txSize);
124
- CFeeRate nNewFeeRate;
125
126
// The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
126
127
// future proof against changes to network wide policy for incremental relay
127
128
// fee that our node may not be aware of.
@@ -131,34 +132,17 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
131
132
walletIncrementalRelayFee = nodeIncrementalRelayFee;
132
133
}
133
134
134
- if (total_fee > 0 ) {
135
- CAmount minTotalFee = nOldFeeRate.GetFee (maxNewTxSize) + nodeIncrementalRelayFee.GetFee (maxNewTxSize);
136
- if (total_fee < minTotalFee) {
137
- errors.push_back (strprintf (" Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)" ,
138
- FormatMoney (minTotalFee), FormatMoney (nOldFeeRate.GetFee (maxNewTxSize)), FormatMoney (nodeIncrementalRelayFee.GetFee (maxNewTxSize))));
139
- return Result::INVALID_PARAMETER;
140
- }
141
- CAmount requiredFee = GetRequiredFee (*wallet, maxNewTxSize);
142
- if (total_fee < requiredFee) {
143
- errors.push_back (strprintf (" Insufficient totalFee (cannot be less than required fee %s)" ,
144
- FormatMoney (requiredFee)));
145
- return Result::INVALID_PARAMETER;
146
- }
147
- new_fee = total_fee;
148
- nNewFeeRate = CFeeRate (total_fee, maxNewTxSize);
149
- } else {
150
- new_fee = GetMinimumFee (*wallet, maxNewTxSize, coin_control, nullptr /* FeeCalculation */ );
151
- nNewFeeRate = CFeeRate (new_fee, maxNewTxSize);
152
-
153
- // New fee rate must be at least old rate + minimum incremental relay rate
154
- // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
155
- // in that unit (fee per kb).
156
- // However, nOldFeeRate is a calculated value from the tx fee/size, so
157
- // add 1 satoshi to the result, because it may have been rounded down.
158
- if (nNewFeeRate.GetFeePerK () < nOldFeeRate.GetFeePerK () + 1 + walletIncrementalRelayFee.GetFeePerK ()) {
159
- nNewFeeRate = CFeeRate (nOldFeeRate.GetFeePerK () + 1 + walletIncrementalRelayFee.GetFeePerK ());
160
- new_fee = nNewFeeRate.GetFee (maxNewTxSize);
161
- }
135
+ CAmount minTotalFee = nOldFeeRate.GetFee (maxNewTxSize) + nodeIncrementalRelayFee.GetFee (maxNewTxSize);
136
+ if (total_fee < minTotalFee) {
137
+ errors.push_back (strprintf (" Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)" ,
138
+ FormatMoney (minTotalFee), FormatMoney (nOldFeeRate.GetFee (maxNewTxSize)), FormatMoney (nodeIncrementalRelayFee.GetFee (maxNewTxSize))));
139
+ return Result::INVALID_PARAMETER;
140
+ }
141
+ CAmount requiredFee = GetRequiredFee (*wallet, maxNewTxSize);
142
+ if (total_fee < requiredFee) {
143
+ errors.push_back (strprintf (" Insufficient totalFee (cannot be less than required fee %s)" ,
144
+ FormatMoney (requiredFee)));
145
+ return Result::INVALID_PARAMETER;
162
146
}
163
147
164
148
// Check that in all cases the new fee doesn't violate maxTxFee
@@ -175,14 +159,14 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
175
159
// in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
176
160
// moment earlier. In this case, we report an error to the user, who may use total_fee to make an adjustment.
177
161
CFeeRate minMempoolFeeRate = wallet->chain ().mempoolMinFee ();
162
+ CFeeRate nNewFeeRate = CFeeRate (total_fee, maxNewTxSize);
178
163
if (nNewFeeRate.GetFeePerK () < minMempoolFeeRate.GetFeePerK ()) {
179
164
errors.push_back (strprintf (
180
165
" New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "
181
- " the totalFee value should be at least %s or the settxfee value should be at least %s to add transaction" ,
166
+ " the totalFee value should be at least %s to add transaction" ,
182
167
FormatMoney (nNewFeeRate.GetFeePerK ()),
183
168
FormatMoney (minMempoolFeeRate.GetFeePerK ()),
184
- FormatMoney (minMempoolFeeRate.GetFee (maxNewTxSize)),
185
- FormatMoney (minMempoolFeeRate.GetFeePerK ())));
169
+ FormatMoney (minMempoolFeeRate.GetFee (maxNewTxSize))));
186
170
return Result::WALLET_ERROR;
187
171
}
188
172
@@ -212,6 +196,109 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
212
196
}
213
197
}
214
198
199
+ return Result::OK;
200
+ }
201
+
202
+
203
+ Result CreateRateBumpTransaction (CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<std::string>& errors,
204
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
205
+ {
206
+ // We are going to modify coin control later, copy to re-use
207
+ CCoinControl new_coin_control (coin_control);
208
+
209
+ auto locked_chain = wallet->chain ().lock ();
210
+ LOCK (wallet->cs_wallet );
211
+ errors.clear ();
212
+ auto it = wallet->mapWallet .find (txid);
213
+ if (it == wallet->mapWallet .end ()) {
214
+ errors.push_back (" Invalid or non-wallet transaction id" );
215
+ return Result::INVALID_ADDRESS_OR_KEY;
216
+ }
217
+ const CWalletTx& wtx = it->second ;
218
+
219
+ Result result = PreconditionChecks (*locked_chain, wallet, wtx, errors);
220
+ if (result != Result::OK) {
221
+ return result;
222
+ }
223
+
224
+ // Fill in recipients(and preserve a single change key if there is one)
225
+ std::vector<CRecipient> recipients;
226
+ for (const auto & output : wtx.tx ->vout ) {
227
+ if (!wallet->IsChange (output)) {
228
+ CRecipient recipient = {output.scriptPubKey , output.nValue , false };
229
+ recipients.push_back (recipient);
230
+ } else {
231
+ CTxDestination change_dest;
232
+ ExtractDestination (output.scriptPubKey , change_dest);
233
+ new_coin_control.destChange = change_dest;
234
+ }
235
+ }
236
+
237
+ // Get the fee rate of the original transaction. This is calculated from
238
+ // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
239
+ // result.
240
+ old_fee = wtx.GetDebit (ISMINE_SPENDABLE) - wtx.tx ->GetValueOut ();
241
+ int64_t txSize = GetVirtualTransactionSize (*(wtx.tx ));
242
+ // Feerate of thing we are bumping
243
+ CFeeRate feerate (old_fee, txSize);
244
+ feerate += CFeeRate (1 );
245
+
246
+ // The node has a configurable incremental relay fee. Increment the fee by
247
+ // the minimum of that and the wallet's conservative
248
+ // WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to
249
+ // network wide policy for incremental relay fee that our node may not be
250
+ // aware of. This ensures we're over the over the required relay fee rate
251
+ // (BIP 125 rule 4). The replacement tx will be at least as large as the
252
+ // original tx, so the total fee will be greater (BIP 125 rule 3)
253
+ CFeeRate node_incremental_relay_fee = wallet->chain ().relayIncrementalFee ();
254
+ CFeeRate wallet_incremental_relay_fee = CFeeRate (WALLET_INCREMENTAL_RELAY_FEE);
255
+ feerate += std::max (node_incremental_relay_fee, wallet_incremental_relay_fee);
256
+
257
+ // Fee rate must also be at least the wallet's GetMinimumFeeRate
258
+ CFeeRate min_feerate (GetMinimumFeeRate (*wallet, new_coin_control, /* feeCalc */ nullptr ));
259
+
260
+ // Set the required fee rate for the replacement transaction in coin control.
261
+ new_coin_control.m_feerate = std::max (feerate, min_feerate);
262
+
263
+ // Fill in required inputs we are double-spending(all of them)
264
+ // N.B.: bip125 doesn't require all the inputs in the replaced transaction to be
265
+ // used in the replacement transaction, but it's very important for wallets to make
266
+ // sure that happens. If not, it would be possible to bump a transaction A twice to
267
+ // A2 and A3 where A2 and A3 don't conflict (or alternatively bump A to A2 and A2
268
+ // to A3 where A and A3 don't conflict). If both later get confirmed then the sender
269
+ // has accidentally double paid.
270
+ for (const auto & inputs : wtx.tx ->vin ) {
271
+ new_coin_control.Select (COutPoint (inputs.prevout ));
272
+ }
273
+ new_coin_control.fAllowOtherInputs = true ;
274
+
275
+ // We cannot source new unconfirmed inputs(bip125 rule 2)
276
+ new_coin_control.m_min_depth = 1 ;
277
+
278
+ CTransactionRef tx_new = MakeTransactionRef ();
279
+ CReserveKey reservekey (wallet);
280
+ CAmount fee_ret;
281
+ int change_pos_in_out = -1 ; // No requested location for change
282
+ std::string fail_reason;
283
+ if (!wallet->CreateTransaction (*locked_chain, recipients, tx_new, reservekey, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false )) {
284
+ errors.push_back (" Unable to create transaction: " + fail_reason);
285
+ return Result::WALLET_ERROR;
286
+ }
287
+
288
+ // If change key hasn't been ReturnKey'ed by this point, we take it out of keypool
289
+ reservekey.KeepKey ();
290
+
291
+ // Write back new fee if successful
292
+ new_fee = fee_ret;
293
+
294
+ // Write back transaction
295
+ mtx = CMutableTransaction (*tx_new);
296
+ // Mark new tx not replaceable, if requested.
297
+ if (!coin_control.m_signal_bip125_rbf .get_value_or (wallet->m_signal_rbf )) {
298
+ for (auto & input : mtx.vin ) {
299
+ if (input.nSequence < 0xfffffffe ) input.nSequence = 0xfffffffe ;
300
+ }
301
+ }
215
302
216
303
return Result::OK;
217
304
}
0 commit comments