22// Distributed under the MIT software license, see the accompanying
33// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
5+ #include < consensus/validation.h>
56#include < interfaces/chain.h>
67#include < policy/fees.h>
78#include < policy/policy.h>
1920namespace wallet {
2021// ! Check whether transaction has descendant in wallet or mempool, or has been
2122// ! mined, or conflicts with a mined transaction. Return a feebumper::Result.
22- 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)
2324{
2425 if (wallet.HasWalletSpend (wtx.tx )) {
2526 errors.push_back (Untranslated (" Transaction has descendants in the wallet" ));
@@ -48,20 +49,21 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
4849 return feebumper::Result::WALLET_ERROR;
4950 }
5051
51- // check that original tx consists entirely of our inputs
52- // 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)
53- isminefilter filter = wallet.GetLegacyScriptPubKeyMan () && wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
54- if (!AllInputsMine (wallet, *wtx.tx , filter)) {
55- errors.push_back (Untranslated (" Transaction contains inputs that don't belong to this wallet" ));
56- 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+ }
5760 }
5861
59-
6062 return feebumper::Result::OK;
6163}
6264
6365// ! Check if the user provided a valid feeRate
64- static feebumper::Result CheckFeeRate (const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, std::vector<bilingual_str>& errors)
66+ static feebumper::Result CheckFeeRate (const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, CAmount old_fee, std::vector<bilingual_str>& errors)
6567{
6668 // check that fee rate is higher than mempool's minimum fee
6769 // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
@@ -83,8 +85,6 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
8385 CFeeRate incrementalRelayFee = std::max (wallet.chain ().relayIncrementalFee (), CFeeRate (WALLET_INCREMENTAL_RELAY_FEE));
8486
8587 // Given old total fee and transaction size, calculate the old feeRate
86- isminefilter filter = wallet.GetLegacyScriptPubKeyMan () && wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
87- CAmount old_fee = CachedTxGetDebit (wallet, wtx, filter) - wtx.tx ->GetValueOut ();
8888 const int64_t txSize = GetVirtualTransactionSize (*(wtx.tx ));
8989 CFeeRate nOldFeeRate (old_fee, txSize);
9090 // Min total fee is old fee + relay fee
@@ -150,12 +150,12 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
150150 if (wtx == nullptr ) return false ;
151151
152152 std::vector<bilingual_str> errors_dummy;
153- feebumper::Result res = PreconditionChecks (wallet, *wtx, errors_dummy);
153+ feebumper::Result res = PreconditionChecks (wallet, *wtx, /* require_mine= */ true , errors_dummy);
154154 return res == feebumper::Result::OK;
155155}
156156
157157Result CreateRateBumpTransaction (CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
158- CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
158+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine )
159159{
160160 // We are going to modify coin control later, copy to re-use
161161 CCoinControl new_coin_control (coin_control);
@@ -169,13 +169,63 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
169169 }
170170 const CWalletTx& wtx = it->second ;
171171
172- Result result = PreconditionChecks (wallet, wtx, errors);
172+ // Retrieve all of the UTXOs and add them to coin control
173+ // While we're here, calculate the input amount
174+ std::map<COutPoint, Coin> coins;
175+ CAmount input_value = 0 ;
176+ std::vector<CTxOut> spent_outputs;
177+ for (const CTxIn& txin : wtx.tx ->vin ) {
178+ coins[txin.prevout ]; // Create empty map entry keyed by prevout.
179+ }
180+ wallet.chain ().findCoins (coins);
181+ for (const CTxIn& txin : wtx.tx ->vin ) {
182+ const Coin& coin = coins.at (txin.prevout );
183+ if (coin.out .IsNull ()) {
184+ errors.push_back (Untranslated (strprintf (" %s:%u is already spent" , txin.prevout .hash .GetHex (), txin.prevout .n )));
185+ return Result::MISC_ERROR;
186+ }
187+ if (wallet.IsMine (txin.prevout )) {
188+ new_coin_control.Select (txin.prevout );
189+ } else {
190+ new_coin_control.SelectExternal (txin.prevout , coin.out );
191+ }
192+ input_value += coin.out .nValue ;
193+ spent_outputs.push_back (coin.out );
194+ }
195+
196+ // Figure out if we need to compute the input weight, and do so if necessary
197+ PrecomputedTransactionData txdata;
198+ txdata.Init (*wtx.tx , std::move (spent_outputs), /* force=*/ true );
199+ for (unsigned int i = 0 ; i < wtx.tx ->vin .size (); ++i) {
200+ const CTxIn& txin = wtx.tx ->vin .at (i);
201+ const Coin& coin = coins.at (txin.prevout );
202+
203+ if (new_coin_control.IsExternalSelected (txin.prevout )) {
204+ // For external inputs, we estimate the size using the size of this input
205+ int64_t input_weight = GetTransactionInputWeight (txin);
206+ // Because signatures can have different sizes, we need to figure out all of the
207+ // signature sizes and replace them with the max sized signature.
208+ // In order to do this, we verify the script with a special SignatureChecker which
209+ // will observe the signatures verified and record their sizes.
210+ SignatureWeights weights;
211+ TransactionSignatureChecker tx_checker (wtx.tx .get (), i, coin.out .nValue , txdata, MissingDataBehavior::FAIL);
212+ SignatureWeightChecker size_checker (weights, tx_checker);
213+ VerifyScript (txin.scriptSig , coin.out .scriptPubKey , &txin.scriptWitness , STANDARD_SCRIPT_VERIFY_FLAGS, size_checker);
214+ // Add the difference between max and current to input_weight so that it represents the largest the input could be
215+ input_weight += weights.GetWeightDiffToMax ();
216+ new_coin_control.SetInputWeight (txin.prevout , input_weight);
217+ }
218+ }
219+
220+ Result result = PreconditionChecks (wallet, wtx, require_mine, errors);
173221 if (result != Result::OK) {
174222 return result;
175223 }
176224
177225 // Fill in recipients(and preserve a single change key if there is one)
226+ // While we're here, calculate the output amount
178227 std::vector<CRecipient> recipients;
228+ CAmount output_value = 0 ;
179229 for (const auto & output : wtx.tx ->vout ) {
180230 if (!OutputIsChange (wallet, output)) {
181231 CRecipient recipient = {output.scriptPubKey , output.nValue , false };
@@ -185,16 +235,22 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
185235 ExtractDestination (output.scriptPubKey , change_dest);
186236 new_coin_control.destChange = change_dest;
187237 }
238+ output_value += output.nValue ;
188239 }
189240
190- isminefilter filter = wallet.GetLegacyScriptPubKeyMan () && wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
191- old_fee = CachedTxGetDebit (wallet, wtx, filter) - wtx.tx ->GetValueOut ();
241+ old_fee = input_value - output_value;
192242
193243 if (coin_control.m_feerate ) {
194244 // The user provided a feeRate argument.
195245 // We calculate this here to avoid compiler warning on the cs_wallet lock
196- const int64_t maxTxSize{CalculateMaximumSignedTxSize (*wtx.tx , &wallet).vsize };
197- Result res = CheckFeeRate (wallet, wtx, *new_coin_control.m_feerate , maxTxSize, errors);
246+ // We need to make a temporary transaction with no input witnesses as the dummy signer expects them to be empty for external inputs
247+ CMutableTransaction mtx{*wtx.tx };
248+ for (auto & txin : mtx.vin ) {
249+ txin.scriptSig .clear ();
250+ txin.scriptWitness .SetNull ();
251+ }
252+ const int64_t maxTxSize{CalculateMaximumSignedTxSize (CTransaction (mtx), &wallet, &new_coin_control).vsize };
253+ Result res = CheckFeeRate (wallet, wtx, *new_coin_control.m_feerate , maxTxSize, old_fee, errors);
198254 if (res != Result::OK) {
199255 return res;
200256 }
@@ -254,7 +310,7 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti
254310 const CWalletTx& oldWtx = it->second ;
255311
256312 // make sure the transaction still has no descendants and hasn't been mined in the meantime
257- Result result = PreconditionChecks (wallet, oldWtx, errors);
313+ Result result = PreconditionChecks (wallet, oldWtx, /* require_mine= */ false , errors);
258314 if (result != Result::OK) {
259315 return result;
260316 }
0 commit comments