Skip to content

Commit

Permalink
wallet: Move fee underpayment check to after fee setting
Browse files Browse the repository at this point in the history
It doesn't make sense to be checking whether the fee paid is underpaying
before we've finished setting the fees. So do that after we have done
the reduction for SFFO and change adjustment for fee overpayment.
  • Loading branch information
achow101 committed Dec 9, 2022
1 parent e5daf97 commit c1a84f1
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 9 deletions.
7 changes: 7 additions & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <ios>
#include <limits>
#include <memory>
#include <numeric>
#include <string>
#include <tuple>
#include <utility>
Expand Down Expand Up @@ -280,6 +281,12 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
s << tx.nLockTime;
}

template<typename TxType>
inline CAmount CalculateOutputValue(const TxType& tx)
{
return std::accumulate(tx.vout.cbegin(), tx.vout.cend(), CAmount{0}, [](CAmount sum, const auto& txout) { return sum + txout.nValue; });
}


/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
Expand Down
27 changes: 18 additions & 9 deletions src/wallet/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <interfaces/chain.h>
#include <numeric>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/signingprovider.h>
#include <util/check.h>
#include <util/fees.h>
Expand Down Expand Up @@ -959,19 +960,18 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
return util::Error{_("Missing solving data for estimating transaction size")};
}
CAmount fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
CAmount current_fee = result->GetSelectedValue() - recipients_sum - change_amount;

// The only time that fee_needed should be less than the amount available for fees is when
// we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug.
if (!coin_selection_params.m_subtract_fee_outputs && fee_needed > current_fee) {
return util::Error{Untranslated(STR_INTERNAL_BUG("Fee needed > fee paid"))};
}
const CAmount output_value = CalculateOutputValue(txNew);
Assume(recipients_sum + change_amount == output_value);
CAmount current_fee = result->GetSelectedValue() - output_value;

// If there is a change output and we overpay the fees then increase the change to match the fee needed
if (nChangePosInOut != -1 && fee_needed < current_fee) {
auto& change = txNew.vout.at(nChangePosInOut);
change.nValue += current_fee - fee_needed;
current_fee = fee_needed;
current_fee = result->GetSelectedValue() - CalculateOutputValue(txNew);
if (fee_needed != current_fee) {
return util::Error{Untranslated(STR_INTERNAL_BUG("Change adjustment: Fee needed != fee paid"))};
}
}

// Reduce output values for subtractFeeFromAmount
Expand Down Expand Up @@ -1007,7 +1007,16 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
}
++i;
}
current_fee = fee_needed;
current_fee = result->GetSelectedValue() - CalculateOutputValue(txNew);
if (fee_needed != current_fee) {
return util::Error{Untranslated(STR_INTERNAL_BUG("SFFO: Fee needed != fee paid"))};
}
}

// fee_needed should now always be less than or equal to the current fees that we pay.
// If it is not, it is a bug.
if (fee_needed > current_fee) {
return util::Error{Untranslated(STR_INTERNAL_BUG("Fee needed > fee paid"))};
}

// Give up if change keypool ran out and change is required
Expand Down

0 comments on commit c1a84f1

Please sign in to comment.