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