Skip to content

Commit 9c833f4

Browse files
committed
Merge #11145: Fix rounding bug in calculation of minimum change
6af49dd Output a bit more information for fee calculation report. (Alex Morcos) a54c7b9 Fix rounding errors in calculation of minimum change size (Alex Morcos) Pull request description: Thanks to @juscamarena for reporting this. Please backport to 0.15. There was a potential rounding error where the fee for the change added to the fee for the original tx could be less than the fee for the tx including change. This is fixed in the first commit. The second commit adds one more snippet of information in the fee calculation report. I actually realized that there is more information that would be nice to report, but we can add that post 0.15. An open question is whether we should be returning failure if the test in line 2885 is hit or just resetting pick_new_inputs and continuing. Originally I made it a failure to avoid any possible infinite loops. But the case hit here is an example of where that logic possibly backfired. Tree-SHA512: efe049781acc1f6a8ad429a689359ac6f7b7c44cdfc9578a866dff4a2f6596e8de474a89d25c704f31ef4f8c89af770e98b75ef06c25419d5a6dfc87247bf274
2 parents 745bbdc + 6af49dd commit 9c833f4

File tree

1 file changed

+13
-10
lines changed

1 file changed

+13
-10
lines changed

src/wallet/wallet.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,6 +2612,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
26122612
assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
26132613
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
26142614
FeeCalculation feeCalc;
2615+
CAmount nFeeNeeded;
26152616
unsigned int nBytes;
26162617
{
26172618
std::set<CInputCoin> setCoins;
@@ -2773,7 +2774,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
27732774
vin.scriptWitness.SetNull();
27742775
}
27752776

2776-
CAmount nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
2777+
nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
27772778

27782779
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
27792780
// because we must be at the maximum allowed fee.
@@ -2794,13 +2795,15 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
27942795
// new inputs. We now know we only need the smaller fee
27952796
// (because of reduced tx size) and so we should add a
27962797
// change output. Only try this once.
2797-
CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr);
2798-
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
2799-
CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change;
2800-
if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
2801-
pick_new_inputs = false;
2802-
nFeeRet = nFeeNeeded + fee_needed_for_change;
2803-
continue;
2798+
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
2799+
unsigned int tx_size_with_change = nBytes + change_prototype_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
2800+
CAmount fee_needed_with_change = GetMinimumFee(tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr);
2801+
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
2802+
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
2803+
pick_new_inputs = false;
2804+
nFeeRet = fee_needed_with_change;
2805+
continue;
2806+
}
28042807
}
28052808

28062809
// If we have change output already, just increase it
@@ -2895,8 +2898,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
28952898
}
28962899
}
28972900

2898-
LogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
2899-
nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
2901+
LogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
2902+
nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
29002903
feeCalc.est.pass.start, feeCalc.est.pass.end,
29012904
100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool),
29022905
feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool,

0 commit comments

Comments
 (0)