Skip to content

Commit c1a84f1

Browse files
committed
wallet: Move fee underpayment check to after fee setting
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.
1 parent e5daf97 commit c1a84f1

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

src/primitives/transaction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <ios>
1818
#include <limits>
1919
#include <memory>
20+
#include <numeric>
2021
#include <string>
2122
#include <tuple>
2223
#include <utility>
@@ -280,6 +281,12 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
280281
s << tx.nLockTime;
281282
}
282283

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

284291
/** The basic transaction that is broadcasted on the network and contained in
285292
* blocks. A transaction can contain multiple inputs and outputs.

src/wallet/spend.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <interfaces/chain.h>
99
#include <numeric>
1010
#include <policy/policy.h>
11+
#include <primitives/transaction.h>
1112
#include <script/signingprovider.h>
1213
#include <util/check.h>
1314
#include <util/fees.h>
@@ -959,19 +960,18 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
959960
return util::Error{_("Missing solving data for estimating transaction size")};
960961
}
961962
CAmount fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
962-
CAmount current_fee = result->GetSelectedValue() - recipients_sum - change_amount;
963-
964-
// The only time that fee_needed should be less than the amount available for fees is when
965-
// we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug.
966-
if (!coin_selection_params.m_subtract_fee_outputs && fee_needed > current_fee) {
967-
return util::Error{Untranslated(STR_INTERNAL_BUG("Fee needed > fee paid"))};
968-
}
963+
const CAmount output_value = CalculateOutputValue(txNew);
964+
Assume(recipients_sum + change_amount == output_value);
965+
CAmount current_fee = result->GetSelectedValue() - output_value;
969966

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

977977
// Reduce output values for subtractFeeFromAmount
@@ -1007,7 +1007,16 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
10071007
}
10081008
++i;
10091009
}
1010-
current_fee = fee_needed;
1010+
current_fee = result->GetSelectedValue() - CalculateOutputValue(txNew);
1011+
if (fee_needed != current_fee) {
1012+
return util::Error{Untranslated(STR_INTERNAL_BUG("SFFO: Fee needed != fee paid"))};
1013+
}
1014+
}
1015+
1016+
// fee_needed should now always be less than or equal to the current fees that we pay.
1017+
// If it is not, it is a bug.
1018+
if (fee_needed > current_fee) {
1019+
return util::Error{Untranslated(STR_INTERNAL_BUG("Fee needed > fee paid"))};
10111020
}
10121021

10131022
// Give up if change keypool ran out and change is required

0 commit comments

Comments
 (0)