Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2017-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <wallet/feebumper.h>7#include <coins.h>8#include <common/system.h>9#include <consensus/validation.h>10#include <interfaces/chain.h>11#include <policy/policy.h>12#include <util/moneystr.h>13#include <util/rbf.h>14#include <util/translation.h>15#include <wallet/coincontrol.h>16#include <wallet/fees.h>17#include <wallet/receive.h>18#include <wallet/spend.h>19#include <wallet/wallet.h>21namespace wallet {22//! Check whether transaction has descendant in wallet or mempool, or has been23//! mined, or conflicts with a mined transaction. Return a feebumper::Result.24static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, bool require_mine, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)25{26if (wallet.HasWalletSpend(wtx.GetTx())) {27errors.emplace_back(Untranslated("Transaction has descendants in the wallet"));28return feebumper::Result::INVALID_PARAMETER;29}31{32if (wallet.chain().hasDescendantsInMempool(wtx.GetHash())) {33errors.emplace_back(Untranslated("Transaction has descendants in the mempool"));34return feebumper::Result::INVALID_PARAMETER;35}36}38if (wallet.GetTxDepthInMainChain(wtx) != 0) {39errors.emplace_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction"));40return feebumper::Result::WALLET_ERROR;41}43if (wtx.m_replaced_by_txid) {44errors.push_back(Untranslated(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", wtx.GetHash().ToString(), wtx.m_replaced_by_txid->ToString())));45return feebumper::Result::WALLET_ERROR;46}48if (require_mine) {49// check that original tx consists entirely of our inputs50// 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)51if (!AllInputsMine(wallet, *wtx.GetTx())) {52errors.emplace_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));53return feebumper::Result::WALLET_ERROR;54}55}57return feebumper::Result::OK;58}60//! Check if the user provided a valid feeRate61static feebumper::Result CheckFeeRate(const CWallet& wallet, const CMutableTransaction& mtx, const CFeeRate& newFeerate, const int64_t maxTxSize, CAmount old_fee, std::vector<bilingual_str>& errors)62{63// check that fee rate is higher than mempool's minimum fee64// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)65// This may occur if fallbackfee is too low, or, perhaps,66// in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a67// moment earlier. In this case, we report an error to the user, who may adjust the fee.68CFeeRate minMempoolFeeRate = wallet.chain().mempoolMinFee();70if (newFeerate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {71errors.push_back(Untranslated(72strprintf("New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- ",73FormatMoney(newFeerate.GetFeePerK()),74FormatMoney(minMempoolFeeRate.GetFeePerK()))));75return feebumper::Result::WALLET_ERROR;76}78std::vector<COutPoint> reused_inputs;79reused_inputs.reserve(mtx.vin.size());80for (const CTxIn& txin : mtx.vin) {81reused_inputs.push_back(txin.prevout);82}84const std::optional<CAmount> combined_bump_fee = wallet.chain().calculateCombinedBumpFee(reused_inputs, newFeerate);85if (!combined_bump_fee.has_value()) {86errors.push_back(Untranslated(strprintf("Failed to calculate bump fees, because unconfirmed UTXOs depend on an enormous cluster of unconfirmed transactions.")));87return feebumper::Result::WALLET_ERROR;88}89CAmount new_total_fee = newFeerate.GetFee(maxTxSize) + combined_bump_fee.value();91CFeeRate incrementalRelayFee = wallet.chain().relayIncrementalFee();93// Min total fee is old fee + relay fee94CAmount minTotalFee = old_fee + incrementalRelayFee.GetFee(maxTxSize);96if (new_total_fee < minTotalFee) {97errors.push_back(Untranslated(strprintf("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)",98FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(old_fee), FormatMoney(incrementalRelayFee.GetFee(maxTxSize)))));99return feebumper::Result::INVALID_PARAMETER;100}102CAmount requiredFee = GetRequiredFee(wallet, maxTxSize);103if (new_total_fee < requiredFee) {104errors.push_back(Untranslated(strprintf("Insufficient total fee (cannot be less than required fee %s)",105FormatMoney(requiredFee))));106return feebumper::Result::INVALID_PARAMETER;107}109// Check that in all cases the new fee doesn't violate maxTxFee110const CAmount max_tx_fee = wallet.m_default_max_tx_fee;111if (new_total_fee > max_tx_fee) {112errors.push_back(Untranslated(strprintf("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)",113FormatMoney(new_total_fee), FormatMoney(max_tx_fee))));114return feebumper::Result::WALLET_ERROR;115}117return feebumper::Result::OK;118}120static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, const CCoinControl& coin_control)121{122// Get the fee rate of the original transaction. This is calculated from123// the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the124// result.125int64_t txSize = GetVirtualTransactionSize(*(wtx.GetTx()));126CFeeRate feerate(old_fee, txSize);127feerate += CFeeRate(1);129// The node has a configurable incremental relay fee. Increment the fee by130// the minimum of that and the wallet's conservative131// WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to132// network wide policy for incremental relay fee that our node may not be133// aware of. This ensures we're over the required relay fee rate134// (Rule 4). The replacement tx will be at least as large as the135// original tx, so the total fee will be greater (Rule 3)136CFeeRate node_incremental_relay_fee = wallet.chain().relayIncrementalFee();137CFeeRate wallet_incremental_relay_fee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);138feerate += std::max(node_incremental_relay_fee, wallet_incremental_relay_fee);140// Fee rate must also be at least the wallet's GetMinimumFeeRate141CFeeRate min_feerate(GetMinimumFeeRate(wallet, coin_control).fee_rate);143// Set the required fee rate for the replacement transaction in coin control.144return std::max(feerate, min_feerate);145}147namespace feebumper {149bool TransactionCanBeBumped(const CWallet& wallet, const Txid& txid)150{151LOCK(wallet.cs_wallet);152const CWalletTx* wtx = wallet.GetWalletTx(txid);153if (wtx == nullptr) return false;155std::vector<bilingual_str> errors_dummy;156feebumper::Result res = PreconditionChecks(wallet, *wtx, /* require_mine=*/ true, errors_dummy);157return res == feebumper::Result::OK;158}160Result CreateRateBumpTransaction(CWallet& wallet, const Txid& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,161CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, const std::vector<CTxOut>& outputs, std::optional<uint32_t> original_change_index)162{163// For now, cannot specify both new outputs to use and an output index to send change164if (!outputs.empty() && original_change_index.has_value()) {165errors.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."));166return Result::INVALID_PARAMETER;167}169// We are going to modify coin control later, copy to reuse170CCoinControl new_coin_control(coin_control);172LOCK(wallet.cs_wallet);173errors.clear();174auto it = wallet.mapWallet.find(txid);175if (it == wallet.mapWallet.end()) {176errors.emplace_back(Untranslated("Invalid or non-wallet transaction id"));177return Result::INVALID_ADDRESS_OR_KEY;178}179const CWalletTx& wtx = it->second;180const CTransactionRef& tx = wtx.GetTx();182// Make sure that original_change_index is valid183if (original_change_index.has_value() && original_change_index.value() >= tx->vout.size()) {184errors.emplace_back(Untranslated("Change position is out of range"));185return Result::INVALID_PARAMETER;186}188// Retrieve all of the UTXOs and add them to coin control189// While we're here, calculate the input amount190std::map<COutPoint, Coin> coins;191CAmount input_value = 0;192std::vector<CTxOut> spent_outputs;193for (const CTxIn& txin : tx->vin) {194coins[txin.prevout]; // Create empty map entry keyed by prevout.195}196wallet.chain().findCoins(coins);197for (const CTxIn& txin : tx->vin) {198const Coin& coin = coins.at(txin.prevout);199if (coin.out.IsNull()) {200errors.emplace_back(Untranslated(strprintf("%s:%u is already spent", txin.prevout.hash.GetHex(), txin.prevout.n)));201return Result::MISC_ERROR;202}203PreselectedInput& preset_txin = new_coin_control.Select(txin.prevout);204if (!wallet.IsMine(txin.prevout)) {205preset_txin.SetTxOut(coin.out);206}207input_value += coin.out.nValue;208spent_outputs.push_back(coin.out);209}211// Figure out if we need to compute the input weight, and do so if necessary212PrecomputedTransactionData txdata;213txdata.Init(*tx, std::move(spent_outputs), /* force=*/ true);214for (unsigned int i = 0; i < tx->vin.size(); ++i) {215const CTxIn& txin = tx->vin.at(i);216const Coin& coin = coins.at(txin.prevout);218if (new_coin_control.IsExternalSelected(txin.prevout)) {219// For external inputs, we estimate the size using the size of this input220int64_t input_weight = GetTransactionInputWeight(txin);221// Because signatures can have different sizes, we need to figure out all of the222// signature sizes and replace them with the max sized signature.223// In order to do this, we verify the script with a special SignatureChecker which224// will observe the signatures verified and record their sizes.225SignatureWeights weights;226TransactionSignatureChecker tx_checker(tx.get(), i, coin.out.nValue, txdata, MissingDataBehavior::FAIL);227SignatureWeightChecker size_checker(weights, tx_checker);228VerifyScript(txin.scriptSig, coin.out.scriptPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, size_checker);229// Add the difference between max and current to input_weight so that it represents the largest the input could be230input_weight += weights.GetWeightDiffToMax();231new_coin_control.SetInputWeight(txin.prevout, input_weight);232}233}235Result result = PreconditionChecks(wallet, wtx, require_mine, errors);236if (result != Result::OK) {237return result;238}240// Calculate the old output amount.241CAmount output_value = 0;242for (const auto& old_output : tx->vout) {243output_value += old_output.nValue;244}246old_fee = input_value - output_value;248// Fill in recipients (and preserve a single change key if there249// is one). If outputs vector is non-empty, replace original250// outputs with its contents, otherwise use original outputs.