@@ -75,9 +75,11 @@ bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid)
7575 return res == feebumper::Result::OK;
7676}
7777
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)
8080{
81+ new_fee = total_fee;
82+
8183 auto locked_chain = wallet->chain ().lock ();
8284 LOCK (wallet->cs_wallet );
8385 errors.clear ();
@@ -121,7 +123,6 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
121123 // calculate the old fee and fee-rate
122124 old_fee = wtx.GetDebit (ISMINE_SPENDABLE) - wtx.tx ->GetValueOut ();
123125 CFeeRate nOldFeeRate (old_fee, txSize);
124- CFeeRate nNewFeeRate;
125126 // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
126127 // future proof against changes to network wide policy for incremental relay
127128 // fee that our node may not be aware of.
@@ -131,34 +132,17 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
131132 walletIncrementalRelayFee = nodeIncrementalRelayFee;
132133 }
133134
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;
162146 }
163147
164148 // 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
175159 // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
176160 // moment earlier. In this case, we report an error to the user, who may use total_fee to make an adjustment.
177161 CFeeRate minMempoolFeeRate = wallet->chain ().mempoolMinFee ();
162+ CFeeRate nNewFeeRate = CFeeRate (total_fee, maxNewTxSize);
178163 if (nNewFeeRate.GetFeePerK () < minMempoolFeeRate.GetFeePerK ()) {
179164 errors.push_back (strprintf (
180165 " 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" ,
182167 FormatMoney (nNewFeeRate.GetFeePerK ()),
183168 FormatMoney (minMempoolFeeRate.GetFeePerK ()),
184- FormatMoney (minMempoolFeeRate.GetFee (maxNewTxSize)),
185- FormatMoney (minMempoolFeeRate.GetFeePerK ())));
169+ FormatMoney (minMempoolFeeRate.GetFee (maxNewTxSize))));
186170 return Result::WALLET_ERROR;
187171 }
188172
@@ -212,6 +196,109 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
212196 }
213197 }
214198
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+ }
215302
216303 return Result::OK;
217304}
0 commit comments