@@ -43,72 +43,72 @@ static int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWalle
43
43
return GetVirtualTransactionSize (txNew);
44
44
}
45
45
46
- namespace feebumper {
47
-
48
- bool FeeBumper::preconditionChecks (const CWallet *wallet, const CWalletTx& wtx) {
46
+ // ! Check whether transaction has descendant in wallet or mempool, or has been
47
+ // ! mined, or conflicts with a mined transaction. Return a feebumper::Result.
48
+ static feebumper::Result PreconditionChecks (const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors)
49
+ {
49
50
if (wallet->HasWalletSpend (wtx.GetHash ())) {
50
51
errors.push_back (" Transaction has descendants in the wallet" );
51
- current_result = Result::INVALID_PARAMETER;
52
- return false ;
52
+ return feebumper::Result::INVALID_PARAMETER;
53
53
}
54
54
55
55
{
56
56
LOCK (mempool.cs );
57
57
auto it_mp = mempool.mapTx .find (wtx.GetHash ());
58
58
if (it_mp != mempool.mapTx .end () && it_mp->GetCountWithDescendants () > 1 ) {
59
59
errors.push_back (" Transaction has descendants in the mempool" );
60
- current_result = Result::INVALID_PARAMETER;
61
- return false ;
60
+ return feebumper::Result::INVALID_PARAMETER;
62
61
}
63
62
}
64
63
65
64
if (wtx.GetDepthInMainChain () != 0 ) {
66
65
errors.push_back (" Transaction has been mined, or is conflicted with a mined transaction" );
67
- current_result = Result::WALLET_ERROR;
68
- return false ;
66
+ return feebumper::Result::WALLET_ERROR;
69
67
}
70
- return true ;
68
+ return feebumper::Result::OK ;
71
69
}
72
70
73
- FeeBumper::FeeBumper (const CWallet *wallet, const uint256 txid_in, const CCoinControl& coin_control, CAmount total_fee)
74
- :
75
- txid (std::move(txid_in)),
76
- old_fee (0 ),
77
- new_fee (0 )
71
+ namespace feebumper {
72
+
73
+ bool TransactionCanBeBumped (CWallet* wallet, const uint256& txid)
78
74
{
75
+ LOCK2 (cs_main, wallet->cs_wallet );
76
+ const CWalletTx* wtx = wallet->GetWalletTx (txid);
77
+ return wtx && SignalsOptInRBF (*wtx->tx ) && !wtx->mapValue .count (" replaced_by_txid" );
78
+ }
79
+
80
+ Result CreateTransaction (const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
81
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
82
+ {
83
+ LOCK2 (cs_main, wallet->cs_wallet );
79
84
errors.clear ();
80
- bumped_txid.SetNull ();
81
- AssertLockHeld (wallet->cs_wallet );
82
85
auto it = wallet->mapWallet .find (txid);
83
86
if (it == wallet->mapWallet .end ()) {
84
87
errors.push_back (" Invalid or non-wallet transaction id" );
85
- current_result = Result::INVALID_ADDRESS_OR_KEY;
86
- return ;
88
+ return Result::INVALID_ADDRESS_OR_KEY;
87
89
}
88
90
const CWalletTx& wtx = it->second ;
89
91
90
- if (!preconditionChecks (wallet, wtx)) {
91
- return ;
92
+ Result result = PreconditionChecks (wallet, wtx, errors);
93
+ if (result != Result::OK) {
94
+ return result;
92
95
}
93
96
94
97
if (!SignalsOptInRBF (*wtx.tx )) {
95
98
errors.push_back (" Transaction is not BIP 125 replaceable" );
96
- current_result = Result::WALLET_ERROR;
97
- return ;
99
+ return Result::WALLET_ERROR;
98
100
}
99
101
100
102
if (wtx.mapValue .count (" replaced_by_txid" )) {
101
103
errors.push_back (strprintf (" Cannot bump transaction %s which was already bumped by transaction %s" , txid.ToString (), wtx.mapValue .at (" replaced_by_txid" )));
102
- current_result = Result::WALLET_ERROR;
103
- return ;
104
+ return Result::WALLET_ERROR;
104
105
}
105
106
106
107
// check that original tx consists entirely of our inputs
107
108
// 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)
108
109
if (!wallet->IsAllFromMe (*wtx.tx , ISMINE_SPENDABLE)) {
109
110
errors.push_back (" Transaction contains inputs that don't belong to this wallet" );
110
- current_result = Result::WALLET_ERROR;
111
- return ;
111
+ return Result::WALLET_ERROR;
112
112
}
113
113
114
114
// figure out which output was change
@@ -118,25 +118,22 @@ FeeBumper::FeeBumper(const CWallet *wallet, const uint256 txid_in, const CCoinCo
118
118
if (wallet->IsChange (wtx.tx ->vout [i])) {
119
119
if (nOutput != -1 ) {
120
120
errors.push_back (" Transaction has multiple change outputs" );
121
- current_result = Result::WALLET_ERROR;
122
- return ;
121
+ return Result::WALLET_ERROR;
123
122
}
124
123
nOutput = i;
125
124
}
126
125
}
127
126
if (nOutput == -1 ) {
128
127
errors.push_back (" Transaction does not have a change output" );
129
- current_result = Result::WALLET_ERROR;
130
- return ;
128
+ return Result::WALLET_ERROR;
131
129
}
132
130
133
131
// Calculate the expected size of the new transaction.
134
132
int64_t txSize = GetVirtualTransactionSize (*(wtx.tx ));
135
133
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize (*wtx.tx , wallet);
136
134
if (maxNewTxSize < 0 ) {
137
135
errors.push_back (" Transaction contains inputs that cannot be signed" );
138
- current_result = Result::INVALID_ADDRESS_OR_KEY;
139
- return ;
136
+ return Result::INVALID_ADDRESS_OR_KEY;
140
137
}
141
138
142
139
// calculate the old fee and fee-rate
@@ -156,15 +153,13 @@ FeeBumper::FeeBumper(const CWallet *wallet, const uint256 txid_in, const CCoinCo
156
153
if (total_fee < minTotalFee) {
157
154
errors.push_back (strprintf (" Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)" ,
158
155
FormatMoney (minTotalFee), FormatMoney (nOldFeeRate.GetFee (maxNewTxSize)), FormatMoney (::incrementalRelayFee.GetFee (maxNewTxSize))));
159
- current_result = Result::INVALID_PARAMETER;
160
- return ;
156
+ return Result::INVALID_PARAMETER;
161
157
}
162
158
CAmount requiredFee = GetRequiredFee (maxNewTxSize);
163
159
if (total_fee < requiredFee) {
164
160
errors.push_back (strprintf (" Insufficient totalFee (cannot be less than required fee %s)" ,
165
161
FormatMoney (requiredFee)));
166
- current_result = Result::INVALID_PARAMETER;
167
- return ;
162
+ return Result::INVALID_PARAMETER;
168
163
}
169
164
new_fee = total_fee;
170
165
nNewFeeRate = CFeeRate (total_fee, maxNewTxSize);
@@ -187,8 +182,7 @@ FeeBumper::FeeBumper(const CWallet *wallet, const uint256 txid_in, const CCoinCo
187
182
if (new_fee > maxTxFee) {
188
183
errors.push_back (strprintf (" Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)" ,
189
184
FormatMoney (new_fee), FormatMoney (maxTxFee)));
190
- current_result = Result::WALLET_ERROR;
191
- return ;
185
+ return Result::WALLET_ERROR;
192
186
}
193
187
194
188
// check that fee rate is higher than mempool's minimum fee
@@ -205,8 +199,7 @@ FeeBumper::FeeBumper(const CWallet *wallet, const uint256 txid_in, const CCoinCo
205
199
FormatMoney (minMempoolFeeRate.GetFeePerK ()),
206
200
FormatMoney (minMempoolFeeRate.GetFee (maxNewTxSize)),
207
201
FormatMoney (minMempoolFeeRate.GetFeePerK ())));
208
- current_result = Result::WALLET_ERROR;
209
- return ;
202
+ return Result::WALLET_ERROR;
210
203
}
211
204
212
205
// Now modify the output to increase the fee.
@@ -217,8 +210,7 @@ FeeBumper::FeeBumper(const CWallet *wallet, const uint256 txid_in, const CCoinCo
217
210
CTxOut* poutput = &(mtx.vout [nOutput]);
218
211
if (poutput->nValue < nDelta) {
219
212
errors.push_back (" Change output is too small to bump the fee" );
220
- current_result = Result::WALLET_ERROR;
221
- return ;
213
+ return Result::WALLET_ERROR;
222
214
}
223
215
224
216
// If the output would become dust, discard it (converting the dust to fee)
@@ -236,31 +228,31 @@ FeeBumper::FeeBumper(const CWallet *wallet, const uint256 txid_in, const CCoinCo
236
228
}
237
229
}
238
230
239
- current_result = Result::OK;
231
+ return Result::OK;
240
232
}
241
233
242
- bool FeeBumper::signTransaction (CWallet * wallet)
243
- {
244
- return wallet->SignTransaction (mtx);
234
+ bool SignTransaction (CWallet* wallet, CMutableTransaction& mtx) {
235
+ LOCK2 (cs_main, wallet-> cs_wallet );
236
+ return wallet->SignTransaction (mtx);
245
237
}
246
238
247
- bool FeeBumper::commit (CWallet * wallet)
239
+ Result CommitTransaction (CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid )
248
240
{
249
- AssertLockHeld ( wallet->cs_wallet );
250
- if (!errors.empty () || current_result != Result::OK ) {
251
- return false ;
241
+ LOCK2 (cs_main, wallet->cs_wallet );
242
+ if (!errors.empty ()) {
243
+ return Result::MISC_ERROR ;
252
244
}
253
245
auto it = txid.IsNull () ? wallet->mapWallet .end () : wallet->mapWallet .find (txid);
254
246
if (it == wallet->mapWallet .end ()) {
255
247
errors.push_back (" Invalid or non-wallet transaction id" );
256
- current_result = Result::MISC_ERROR;
257
- return false ;
248
+ return Result::MISC_ERROR;
258
249
}
259
250
CWalletTx& oldWtx = it->second ;
260
251
261
252
// make sure the transaction still has no descendants and hasn't been mined in the meantime
262
- if (!preconditionChecks (wallet, oldWtx)) {
263
- return false ;
253
+ Result result = PreconditionChecks (wallet, oldWtx, errors);
254
+ if (result != Result::OK) {
255
+ return result;
264
256
}
265
257
266
258
CWalletTx wtxBumped (wallet, MakeTransactionRef (std::move (mtx)));
@@ -276,14 +268,14 @@ bool FeeBumper::commit(CWallet *wallet)
276
268
if (!wallet->CommitTransaction (wtxBumped, reservekey, g_connman.get (), state)) {
277
269
// NOTE: CommitTransaction never returns false, so this should never happen.
278
270
errors.push_back (strprintf (" The transaction was rejected: %s" , state.GetRejectReason ()));
279
- return false ;
271
+ return Result::WALLET_ERROR ;
280
272
}
281
273
282
274
bumped_txid = wtxBumped.GetHash ();
283
275
if (state.IsInvalid ()) {
284
276
// This can happen if the mempool rejected the transaction. Report
285
277
// what happened in the "errors" response.
286
- errors.push_back (strprintf (" The transaction was rejected: %s" , FormatStateMessage (state)));
278
+ errors.push_back (strprintf (" Error: The transaction was rejected: %s" , FormatStateMessage (state)));
287
279
}
288
280
289
281
// mark the original tx as bumped
@@ -294,7 +286,7 @@ bool FeeBumper::commit(CWallet *wallet)
294
286
// replaced does not succeed for some reason.
295
287
errors.push_back (" Created new bumpfee transaction but could not mark the original transaction as replaced" );
296
288
}
297
- return true ;
289
+ return Result::OK ;
298
290
}
299
291
300
292
} // namespace feebumper
0 commit comments