LCOV - code coverage report
Current view: top level - src/wallet - feebumper.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 15 221 6.8 %
Date: 2023-10-05 12:38:51 Functions: 8 16 50.0 %
Branches: 11 454 2.4 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2017-2022 The Bitcoin Core developers
       2                 :            : // Distributed under the MIT software license, see the accompanying
       3                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :            : 
       5                 :            : #include <common/system.h>
       6                 :            : #include <consensus/validation.h>
       7                 :            : #include <interfaces/chain.h>
       8                 :            : #include <policy/fees.h>
       9                 :            : #include <policy/policy.h>
      10                 :            : #include <util/moneystr.h>
      11                 :            : #include <util/rbf.h>
      12                 :            : #include <util/translation.h>
      13                 :            : #include <wallet/coincontrol.h>
      14                 :            : #include <wallet/feebumper.h>
      15                 :            : #include <wallet/fees.h>
      16                 :            : #include <wallet/receive.h>
      17         [ +  - ]:          2 : #include <wallet/spend.h>
      18         [ +  - ]:          2 : #include <wallet/wallet.h>
      19                 :            : 
      20                 :            : namespace wallet {
      21                 :            : //! Check whether transaction has descendant in wallet or mempool, or has been
      22                 :            : //! mined, or conflicts with a mined transaction. Return a feebumper::Result.
      23                 :          0 : static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, bool require_mine, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
      24                 :            : {
      25         [ #  # ]:          0 :     if (wallet.HasWalletSpend(wtx.tx)) {
      26   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Transaction has descendants in the wallet"));
                   #  # ]
      27                 :          2 :         return feebumper::Result::INVALID_PARAMETER;
      28                 :            :     }
      29                 :            : 
      30                 :            :     {
      31         [ #  # ]:          0 :         if (wallet.chain().hasDescendantsInMempool(wtx.GetHash())) {
      32   [ #  #  #  #  :          0 :             errors.push_back(Untranslated("Transaction has descendants in the mempool"));
                   #  # ]
      33                 :          0 :             return feebumper::Result::INVALID_PARAMETER;
      34                 :            :         }
      35                 :            :     }
      36                 :            : 
      37         [ #  # ]:          0 :     if (wallet.GetTxDepthInMainChain(wtx) != 0) {
      38   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction"));
                   #  # ]
      39                 :          0 :         return feebumper::Result::WALLET_ERROR;
      40                 :            :     }
      41                 :            : 
      42         [ #  # ]:          0 :     if (!SignalsOptInRBF(*wtx.tx)) {
      43   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Transaction is not BIP 125 replaceable"));
                   #  # ]
      44                 :          0 :         return feebumper::Result::WALLET_ERROR;
      45                 :            :     }
      46                 :            : 
      47   [ #  #  #  #  :          0 :     if (wtx.mapValue.count("replaced_by_txid")) {
                   #  # ]
      48   [ #  #  #  #  :          0 :         errors.push_back(strprintf(Untranslated("Cannot bump transaction %s which was already bumped by transaction %s"), wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid")));
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
      49                 :          0 :         return feebumper::Result::WALLET_ERROR;
      50                 :            :     }
      51                 :            : 
      52         [ #  # ]:          0 :     if (require_mine) {
      53         [ #  # ]:          0 :         // 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         [ #  # ]:          0 :         isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
      56         [ #  # ]:          0 :         if (!AllInputsMine(wallet, *wtx.tx, filter)) {
      57   [ #  #  #  #  :          0 :             errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
                   #  # ]
      58                 :          0 :             return feebumper::Result::WALLET_ERROR;
      59                 :            :         }
      60                 :          0 :     }
      61                 :            : 
      62                 :          0 :     return feebumper::Result::OK;
      63                 :          0 : }
      64                 :            : 
      65                 :            : //! Check if the user provided a valid feeRate
      66                 :          0 : static feebumper::Result CheckFeeRate(const CWallet& wallet, const CMutableTransaction& mtx, const CFeeRate& newFeerate, const int64_t maxTxSize, CAmount old_fee, std::vector<bilingual_str>& errors)
      67                 :            : {
      68                 :            :     // check that fee rate is higher than mempool's minimum fee
      69                 :            :     // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
      70                 :            :     // This may occur if the user set fee_rate or paytxfee too low, if fallbackfee is too low, or, perhaps,
      71                 :            :     // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
      72                 :            :     // moment earlier. In this case, we report an error to the user, who may adjust the fee.
      73                 :          0 :     CFeeRate minMempoolFeeRate = wallet.chain().mempoolMinFee();
      74                 :          2 : 
      75         [ #  # ]:          0 :     if (newFeerate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
      76   [ #  #  #  # ]:          0 :         errors.push_back(strprintf(
      77   [ #  #  #  # ]:          0 :             Untranslated("New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "),
      78   [ #  #  #  # ]:          0 :             FormatMoney(newFeerate.GetFeePerK()),
      79   [ #  #  #  # ]:          0 :             FormatMoney(minMempoolFeeRate.GetFeePerK())));
      80                 :          0 :         return feebumper::Result::WALLET_ERROR;
      81                 :            :     }
      82                 :            : 
      83                 :          0 :     std::vector<COutPoint> reused_inputs;
      84         [ #  # ]:          0 :     reused_inputs.reserve(mtx.vin.size());
      85         [ #  # ]:          0 :     for (const CTxIn& txin : mtx.vin) {
      86         [ #  # ]:          0 :         reused_inputs.push_back(txin.prevout);
      87                 :            :     }
      88                 :            : 
      89         [ #  # ]:          0 :     std::optional<CAmount> combined_bump_fee = wallet.chain().CalculateCombinedBumpFee(reused_inputs, newFeerate);
      90         [ #  # ]:          0 :     if (!combined_bump_fee.has_value()) {
      91   [ #  #  #  #  :          2 :         errors.push_back(strprintf(Untranslated("Failed to calculate bump fees, because unconfirmed UTXOs depend on enormous cluster of unconfirmed transactions.")));
             #  #  #  # ]
      92                 :          0 :     }
      93   [ #  #  #  # ]:          0 :     CAmount new_total_fee = newFeerate.GetFee(maxTxSize) + combined_bump_fee.value();
      94                 :            : 
      95   [ #  #  #  #  :          0 :     CFeeRate incrementalRelayFee = std::max(wallet.chain().relayIncrementalFee(), CFeeRate(WALLET_INCREMENTAL_RELAY_FEE));
                   #  # ]
      96                 :            : 
      97                 :            :     // Min total fee is old fee + relay fee
      98         [ #  # ]:          0 :     CAmount minTotalFee = old_fee + incrementalRelayFee.GetFee(maxTxSize);
      99                 :          2 : 
     100         [ #  # ]:          0 :     if (new_total_fee < minTotalFee) {
     101   [ #  #  #  #  :          0 :         errors.push_back(strprintf(Untranslated("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)"),
             #  #  #  # ]
     102   [ #  #  #  #  :          0 :             FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(old_fee), FormatMoney(incrementalRelayFee.GetFee(maxTxSize))));
          #  #  #  #  #  
                      # ]
     103                 :          0 :         return feebumper::Result::INVALID_PARAMETER;
     104                 :            :     }
     105                 :            : 
     106         [ #  # ]:          0 :     CAmount requiredFee = GetRequiredFee(wallet, maxTxSize);
     107         [ #  # ]:          0 :     if (new_total_fee < requiredFee) {
     108   [ #  #  #  #  :          0 :         errors.push_back(strprintf(Untranslated("Insufficient total fee (cannot be less than required fee %s)"),
             #  #  #  # ]
     109         [ #  # ]:          0 :             FormatMoney(requiredFee)));
     110                 :          0 :         return feebumper::Result::INVALID_PARAMETER;
     111                 :            :     }
     112                 :            : 
     113                 :            :     // Check that in all cases the new fee doesn't violate maxTxFee
     114                 :          0 :     const CAmount max_tx_fee = wallet.m_default_max_tx_fee;
     115         [ #  # ]:          0 :     if (new_total_fee > max_tx_fee) {
     116   [ #  #  #  #  :          0 :         errors.push_back(strprintf(Untranslated("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)"),
             #  #  #  # ]
     117   [ #  #  #  # ]:          0 :             FormatMoney(new_total_fee), FormatMoney(max_tx_fee)));
     118                 :          0 :         return feebumper::Result::WALLET_ERROR;
     119                 :            :     }
     120                 :            : 
     121                 :          0 :     return feebumper::Result::OK;
     122                 :          0 : }
     123                 :            : 
     124                 :          0 : static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, const CCoinControl& coin_control)
     125                 :            : {
     126                 :            :     // Get the fee rate of the original transaction. This is calculated from
     127                 :            :     // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
     128                 :            :     // result.
     129                 :          0 :     int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
     130                 :          0 :     CFeeRate feerate(old_fee, txSize);
     131                 :          0 :     feerate += CFeeRate(1);
     132                 :            : 
     133                 :            :     // The node has a configurable incremental relay fee. Increment the fee by
     134                 :            :     // the minimum of that and the wallet's conservative
     135                 :            :     // WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to
     136                 :            :     // network wide policy for incremental relay fee that our node may not be
     137                 :            :     // aware of. This ensures we're over the required relay fee rate
     138                 :            :     // (Rule 4).  The replacement tx will be at least as large as the
     139                 :            :     // original tx, so the total fee will be greater (Rule 3)
     140                 :          0 :     CFeeRate node_incremental_relay_fee = wallet.chain().relayIncrementalFee();
     141                 :          0 :     CFeeRate wallet_incremental_relay_fee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
     142                 :          0 :     feerate += std::max(node_incremental_relay_fee, wallet_incremental_relay_fee);
     143                 :            : 
     144                 :            :     // Fee rate must also be at least the wallet's GetMinimumFeeRate
     145                 :          0 :     CFeeRate min_feerate(GetMinimumFeeRate(wallet, coin_control, /*feeCalc=*/nullptr));
     146                 :            : 
     147                 :            :     // Set the required fee rate for the replacement transaction in coin control.
     148                 :          0 :     return std::max(feerate, min_feerate);
     149                 :            : }
     150                 :            : 
     151                 :            : namespace feebumper {
     152                 :            : 
     153                 :          0 : bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
     154                 :            : {
     155                 :          0 :     LOCK(wallet.cs_wallet);
     156         [ #  # ]:          0 :     const CWalletTx* wtx = wallet.GetWalletTx(txid);
     157         [ #  # ]:          0 :     if (wtx == nullptr) return false;
     158                 :            : 
     159                 :          0 :     std::vector<bilingual_str> errors_dummy;
     160         [ #  # ]:          0 :     feebumper::Result res = PreconditionChecks(wallet, *wtx, /* require_mine=*/ true, errors_dummy);
     161                 :          0 :     return res == feebumper::Result::OK;
     162                 :          0 : }
     163         [ +  - ]:          2 : 
     164         [ +  - ]:          2 : Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
     165         [ +  - ]:          2 :                                  CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, const std::vector<CTxOut>& outputs, std::optional<uint32_t> original_change_index)
     166         [ +  - ]:          2 : {
     167         [ +  - ]:          2 :     // For now, cannot specify both new outputs to use and an output index to send change
     168   [ +  -  #  #  :          2 :     if (!outputs.empty() && original_change_index.has_value()) {
                   #  # ]
     169   [ +  -  #  #  :          2 :         errors.push_back(Untranslated("The options 'outputs' and 'original_change_index' are incompatible. You can only either specify a new set of outputs, or designate a change output to be recycled."));
             #  #  #  # ]
     170         [ +  - ]:          2 :         return Result::INVALID_PARAMETER;
     171                 :            :     }
     172                 :            : 
     173                 :            :     // We are going to modify coin control later, copy to re-use
     174                 :          0 :     CCoinControl new_coin_control(coin_control);
     175                 :            : 
     176   [ #  #  #  # ]:          0 :     LOCK(wallet.cs_wallet);
     177                 :          0 :     errors.clear();
     178         [ #  # ]:          0 :     auto it = wallet.mapWallet.find(txid);
     179         [ #  # ]:          0 :     if (it == wallet.mapWallet.end()) {
     180   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
                   #  # ]
     181                 :          0 :         return Result::INVALID_ADDRESS_OR_KEY;
     182                 :            :     }
     183                 :          0 :     const CWalletTx& wtx = it->second;
     184                 :            : 
     185                 :            :     // Make sure that original_change_index is valid
     186   [ #  #  #  #  :          0 :     if (original_change_index.has_value() && original_change_index.value() >= wtx.tx->vout.size()) {
                   #  # ]
     187   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Change position is out of range"));
                   #  # ]
     188                 :          0 :         return Result::INVALID_PARAMETER;
     189                 :            :     }
     190                 :            : 
     191                 :            :     // Retrieve all of the UTXOs and add them to coin control
     192                 :            :     // While we're here, calculate the input amount
     193                 :          0 :     std::map<COutPoint, Coin> coins;
     194                 :          0 :     CAmount input_value = 0;
     195                 :          0 :     std::vector<CTxOut> spent_outputs;
     196         [ #  # ]:          0 :     for (const CTxIn& txin : wtx.tx->vin) {
     197         [ #  # ]:          0 :         coins[txin.prevout]; // Create empty map entry keyed by prevout.
     198                 :            :     }
     199   [ #  #  #  # ]:          0 :     wallet.chain().findCoins(coins);
     200         [ #  # ]:          0 :     for (const CTxIn& txin : wtx.tx->vin) {
     201         [ #  # ]:          0 :         const Coin& coin = coins.at(txin.prevout);
     202   [ #  #  #  # ]:          0 :         if (coin.out.IsNull()) {
     203   [ #  #  #  #  :          0 :             errors.push_back(Untranslated(strprintf("%s:%u is already spent", txin.prevout.hash.GetHex(), txin.prevout.n)));
             #  #  #  # ]
     204                 :          0 :             return Result::MISC_ERROR;
     205                 :            :         }
     206   [ #  #  #  # ]:          0 :         if (wallet.IsMine(txin.prevout)) {
     207         [ #  # ]:          0 :             new_coin_control.Select(txin.prevout);
     208                 :          0 :         } else {
     209         [ #  # ]:          0 :             new_coin_control.SelectExternal(txin.prevout, coin.out);
     210                 :            :         }
     211                 :          0 :         input_value += coin.out.nValue;
     212         [ #  # ]:          0 :         spent_outputs.push_back(coin.out);
     213                 :            :     }
     214                 :            : 
     215                 :            :     // Figure out if we need to compute the input weight, and do so if necessary
     216         [ #  # ]:          0 :     PrecomputedTransactionData txdata;
     217         [ #  # ]:          0 :     txdata.Init(*wtx.tx, std::move(spent_outputs), /* force=*/ true);
     218         [ #  # ]:          0 :     for (unsigned int i = 0; i < wtx.tx->vin.size(); ++i) {
     219         [ #  # ]:          0 :         const CTxIn& txin = wtx.tx->vin.at(i);
     220         [ #  # ]:          0 :         const Coin& coin = coins.at(txin.prevout);
     221                 :            : 
     222   [ #  #  #  # ]:          0 :         if (new_coin_control.IsExternalSelected(txin.prevout)) {
     223                 :            :             // For external inputs, we estimate the size using the size of this input
     224         [ #  # ]:          0 :             int64_t input_weight = GetTransactionInputWeight(txin);
     225                 :            :             // Because signatures can have different sizes, we need to figure out all of the
     226                 :            :             // signature sizes and replace them with the max sized signature.
     227                 :            :             // In order to do this, we verify the script with a special SignatureChecker which
     228                 :            :             // will observe the signatures verified and record their sizes.
     229                 :          0 :             SignatureWeights weights;
     230         [ #  # ]:          0 :             TransactionSignatureChecker tx_checker(wtx.tx.get(), i, coin.out.nValue, txdata, MissingDataBehavior::FAIL);
     231         [ #  # ]:          0 :             SignatureWeightChecker size_checker(weights, tx_checker);
     232         [ #  # ]:          0 :             VerifyScript(txin.scriptSig, coin.out.scriptPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, size_checker);
     233                 :            :             // Add the difference between max and current to input_weight so that it represents the largest the input could be
     234         [ #  # ]:          0 :             input_weight += weights.GetWeightDiffToMax();
     235         [ #  # ]:          0 :             new_coin_control.SetInputWeight(txin.prevout, input_weight);
     236                 :          0 :         }
     237                 :          0 :     }
     238                 :            : 
     239         [ #  # ]:          0 :     Result result = PreconditionChecks(wallet, wtx, require_mine, errors);
     240         [ #  # ]:          0 :     if (result != Result::OK) {
     241                 :          0 :         return result;
     242                 :            :     }
     243                 :            : 
     244                 :            :     // Calculate the old output amount.
     245                 :          0 :     CAmount output_value = 0;
     246         [ #  # ]:          0 :     for (const auto& old_output : wtx.tx->vout) {
     247                 :          0 :         output_value += old_output.nValue;
     248                 :            :     }
     249                 :            : 
     250                 :          0 :     old_fee = input_value - output_value;
     251                 :            : 
     252                 :            :     // Fill in recipients (and preserve a single change key if there
     253                 :            :     // is one). If outputs vector is non-empty, replace original
     254                 :            :     // outputs with its contents, otherwise use original outputs.
     255                 :          0 :     std::vector<CRecipient> recipients;
     256                 :          0 :     CAmount new_outputs_value = 0;
     257         [ #  # ]:          0 :     const auto& txouts = outputs.empty() ? wtx.tx->vout : outputs;
     258         [ #  # ]:          0 :     for (size_t i = 0; i < txouts.size(); ++i) {
     259         [ #  # ]:          0 :         const CTxOut& output = txouts.at(i);
     260         [ #  # ]:          0 :         CTxDestination dest;
     261         [ #  # ]:          0 :         ExtractDestination(output.scriptPubKey, dest);
     262   [ #  #  #  #  :          0 :         if (original_change_index.has_value() ?  original_change_index.value() == i : OutputIsChange(wallet, output)) {
                   #  # ]
     263         [ #  # ]:          0 :             new_coin_control.destChange = dest;
     264         [ +  - ]:          2 :         } else {
     265         [ #  # ]:          0 :             CRecipient recipient = {dest, output.nValue, false};
     266         [ #  # ]:          0 :             recipients.push_back(recipient);
     267                 :          0 :         }
     268                 :          0 :         new_outputs_value += output.nValue;
     269                 :          0 :     }
     270                 :            : 
     271                 :            :     // If no recipients, means that we are sending coins to a change address
     272         [ #  # ]:          0 :     if (recipients.empty()) {
     273                 :            :         // Just as a sanity check, ensure that the change address exist
     274         [ #  # ]:          0 :         if (std::get_if<CNoDestination>(&new_coin_control.destChange)) {
     275   [ #  #  #  #  :          0 :             errors.emplace_back(Untranslated("Unable to create transaction. Transaction must have at least one recipient"));
                   #  # ]
     276                 :          0 :             return Result::INVALID_PARAMETER;
     277                 :            :         }
     278                 :            : 
     279                 :            :         // Add change as recipient with SFFO flag enabled, so fees are deduced from it.
     280                 :            :         // If the output differs from the original tx output (because the user customized it) a new change output will be created.
     281   [ #  #  #  # ]:          0 :         recipients.emplace_back(CRecipient{new_coin_control.destChange, new_outputs_value, /*fSubtractFeeFromAmount=*/true});
     282         [ #  # ]:          0 :         new_coin_control.destChange = CNoDestination();
     283                 :          0 :     }
     284                 :            : 
     285         [ #  # ]:          0 :     if (coin_control.m_feerate) {
     286                 :            :         // The user provided a feeRate argument.
     287                 :            :         // We calculate this here to avoid compiler warning on the cs_wallet lock
     288                 :            :         // We need to make a temporary transaction with no input witnesses as the dummy signer expects them to be empty for external inputs
     289         [ #  # ]:          0 :         CMutableTransaction temp_mtx{*wtx.tx};
     290         [ #  # ]:          0 :         for (auto& txin : temp_mtx.vin) {
     291         [ #  # ]:          0 :             txin.scriptSig.clear();
     292         [ #  # ]:          0 :             txin.scriptWitness.SetNull();
     293                 :            :         }
     294         [ #  # ]:          0 :         temp_mtx.vout = txouts;
     295   [ #  #  #  # ]:          0 :         const int64_t maxTxSize{CalculateMaximumSignedTxSize(CTransaction(temp_mtx), &wallet, &new_coin_control).vsize};
     296   [ #  #  #  # ]:          0 :         Result res = CheckFeeRate(wallet, temp_mtx, *new_coin_control.m_feerate, maxTxSize, old_fee, errors);
     297         [ #  # ]:          0 :         if (res != Result::OK) {
     298                 :          0 :             return res;
     299                 :            :         }
     300         [ #  # ]:          0 :     } else {
     301                 :            :         // The user did not provide a feeRate argument
     302   [ #  #  #  # ]:          0 :         new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, old_fee, new_coin_control);
     303                 :            :     }
     304                 :            : 
     305                 :            :     // Fill in required inputs we are double-spending(all of them)
     306                 :            :     // N.B.: bip125 doesn't require all the inputs in the replaced transaction to be
     307                 :            :     // used in the replacement transaction, but it's very important for wallets to make
     308                 :            :     // sure that happens. If not, it would be possible to bump a transaction A twice to
     309                 :            :     // A2 and A3 where A2 and A3 don't conflict (or alternatively bump A to A2 and A2
     310                 :            :     // to A3 where A and A3 don't conflict). If both later get confirmed then the sender
     311                 :            :     // has accidentally double paid.
     312         [ #  # ]:          0 :     for (const auto& inputs : wtx.tx->vin) {
     313         [ #  # ]:          0 :         new_coin_control.Select(COutPoint(inputs.prevout));
     314                 :            :     }
     315                 :          0 :     new_coin_control.m_allow_other_inputs = true;
     316                 :            : 
     317                 :            :     // We cannot source new unconfirmed inputs(bip125 rule 2)
     318                 :          0 :     new_coin_control.m_min_depth = 1;
     319                 :            : 
     320                 :          0 :     constexpr int RANDOM_CHANGE_POSITION = -1;
     321         [ #  # ]:          0 :     auto res = CreateTransaction(wallet, recipients, RANDOM_CHANGE_POSITION, new_coin_control, false);
     322         [ #  # ]:          0 :     if (!res) {
     323   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + util::ErrorString(res));
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     324                 :          0 :         return Result::WALLET_ERROR;
     325                 :            :     }
     326                 :            : 
     327         [ #  # ]:          0 :     const auto& txr = *res;
     328                 :            :     // Write back new fee if successful
     329                 :          0 :     new_fee = txr.fee;
     330                 :            : 
     331                 :            :     // Write back transaction
     332         [ #  # ]:          0 :     mtx = CMutableTransaction(*txr.tx);
     333                 :            : 
     334                 :          0 :     return Result::OK;
     335                 :          0 : }
     336                 :            : 
     337                 :          0 : bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx) {
     338                 :          0 :     LOCK(wallet.cs_wallet);
     339                 :            : 
     340   [ #  #  #  # ]:          0 :     if (wallet.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
     341                 :            :         // Make a blank psbt
     342         [ #  # ]:          0 :         PartiallySignedTransaction psbtx(mtx);
     343                 :            : 
     344                 :            :         // First fill transaction with our data without signing,
     345                 :            :         // so external signers are not asked to sign more than once.
     346                 :            :         bool complete;
     347         [ #  # ]:          0 :         wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
     348         [ #  # ]:          0 :         const TransactionError err = wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true /* sign */, false  /* bip32derivs */);
     349         [ #  # ]:          0 :         if (err != TransactionError::OK) return false;
     350         [ #  # ]:          0 :         complete = FinalizeAndExtractPSBT(psbtx, mtx);
     351                 :          0 :         return complete;
     352                 :          0 :     } else {
     353         [ #  # ]:          0 :         return wallet.SignTransaction(mtx);
     354                 :            :     }
     355                 :          0 : }
     356                 :            : 
     357                 :          0 : Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<bilingual_str>& errors, uint256& bumped_txid)
     358                 :            : {
     359                 :          0 :     LOCK(wallet.cs_wallet);
     360         [ #  # ]:          0 :     if (!errors.empty()) {
     361                 :          0 :         return Result::MISC_ERROR;
     362                 :            :     }
     363   [ #  #  #  #  :          0 :     auto it = txid.IsNull() ? wallet.mapWallet.end() : wallet.mapWallet.find(txid);
                   #  # ]
     364         [ #  # ]:          0 :     if (it == wallet.mapWallet.end()) {
     365   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
                   #  # ]
     366                 :          0 :         return Result::MISC_ERROR;
     367                 :            :     }
     368                 :          0 :     const CWalletTx& oldWtx = it->second;
     369                 :            : 
     370                 :            :     // make sure the transaction still has no descendants and hasn't been mined in the meantime
     371         [ #  # ]:          0 :     Result result = PreconditionChecks(wallet, oldWtx, /* require_mine=*/ false, errors);
     372         [ #  # ]:          0 :     if (result != Result::OK) {
     373                 :          0 :         return result;
     374                 :            :     }
     375                 :            : 
     376                 :            :     // commit/broadcast the tx
     377         [ #  # ]:          0 :     CTransactionRef tx = MakeTransactionRef(std::move(mtx));
     378         [ #  # ]:          0 :     mapValue_t mapValue = oldWtx.mapValue;
     379   [ #  #  #  #  :          0 :     mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
             #  #  #  # ]
     380                 :            : 
     381   [ #  #  #  # ]:          0 :     wallet.CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm);
     382                 :            : 
     383                 :            :     // mark the original tx as bumped
     384         [ #  # ]:          0 :     bumped_txid = tx->GetHash();
     385   [ #  #  #  #  :          0 :     if (!wallet.MarkReplaced(oldWtx.GetHash(), bumped_txid)) {
                   #  # ]
     386   [ #  #  #  #  :          0 :         errors.push_back(Untranslated("Created new bumpfee transaction but could not mark the original transaction as replaced"));
                   #  # ]
     387                 :          0 :     }
     388                 :          0 :     return Result::OK;
     389                 :          0 : }
     390                 :            : 
     391                 :            : } // namespace feebumper
     392                 :            : } // namespace wallet

Generated by: LCOV version 1.14