This is an alternative to #10333
See commit messages.
The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs
This is an alternative to #10333
See commit messages.
The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs
2750 | @@ -2744,8 +2751,18 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT 2751 | // TODO: The case where nSubtractFeeFromAmount > 0 remains 2752 | // to be addressed because it requires returning the fee to 2753 | // the payees and not the change output. 2754 | - // TODO: The case where there is no change output remains 2755 | - // to be addressed so we avoid creating too small an output. 2756 | + CAmount max_excess_fee = GetMinimumFee(change_prototype_size, currentConfirmationTarget, ::mempool, ::feeEstimator, nullptr) + 2757 | + GetDustThreshold(change_prototype_txout, ::dustRelayFee); 2758 | + // If we have no change and a big enough excess fee, then
please move first explanation a few lines before for the "increase change" to appropriate spot
please move first explanation a few lines before for the "increase change" to appropriate spot
I think order of the comments does makes sense. First comment describes the normal case, second comment describes unhandled special case, and third comment describes handled special case accompanied by implementation for that case.
I might move the code for the normal case above the code & comments for the two special cases, though this would increase the size of the diff. Just a thought, though.
He moved it, the comment wasn't marked as resolved. Happy with current arrangement.
2770 | @@ -2754,6 +2771,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT 2771 | } 2772 | break; // Done, enough fee included. 2773 | } 2774 | + else if (!pick_new_inputs) { 2775 | + // This shouldn't happen
nit: slightly longer explanation for reader?
"This shouldn't happen, we should have had enough excess fee to pay for the newly created change output."
With fix on the nFeeRet, a very slight increase in fee between estimation calls(can that happen?) will result in this triggering.
yes, i don't think that can happen (a change in fee estimation) but belts and suspenders. In any case will be cleaned up post 0.15 with effective value logic and only calculating the needed fee rate once.
2772 | @@ -2774,6 +2773,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT 2773 | } 2774 | } 2775 | 2776 | + if (nChangePosInOut == -1) reserveKey.ReturnKey(); // Return any reserved key if we don't have change
looks like a capital "K" snuck in here.
weird, i fixed that but must have not committed before push
sorry to both you again on this but it appears to still be there
2760 | + // new inputs. We now know we only need the smaller fee 2761 | + // (because of reduced tx size) and so we should add a 2762 | + // change input. Only try this once. 2763 | + if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { 2764 | + pick_new_inputs = false; 2765 | + nFeeRet = nFeeNeeded;
this needs to be nFeeRet = nFeeNeeded + GetMinimumFee(change_prototype_size, currentConfirmationTarget, ::mempool, ::feeEstimator, nullptr) ; at a minimum, right?
nFeeNeeded at this point will not pay for the new change output.
oops, correct
imo this is superior in its simplicity and targeting the exact situation better over #10333 .
A bit worried about the failure case triggering anytime fee estimations change. I know you don't want to in this PR, but pulling out the estimation and getting a feerate that is reapplied in the main loop seems worth it, is easy to review, and will have to be done anyways for effective value work.
Fixed bug and nits and squashed @instagibbs I'm fine with doing the GetMinimumFeeRate refactor for 0.15, but I already did a lot of other work on GetMinimumFee in #10706 which I would really like to get in for 0.15, so I'm wary of piling on too many changes that touch the same lines of code and not being able to get them all in.
All return points should call reservekey.ReturnKey()?
utACK.
IMO going for one last iteration and to have one more flag is a clear sign that a more thoughtful refactor should be done. Not that I'm against this change, but reading this code should be plain and easy.
@promag this is a bugfix with limited scope. Switching to effective value calculation fixes this in a more natural fashion. See: #10360 and https://github.com/bitcoin/bitcoin/pull/10637
Nit, should variables be in camel or snake case? Also could you fix brace styles for the moved code?
@promag New variables are supposed to be snake_case, did I miss one? If there are any other changes, I can fix the braces, but I didn't touch the moved code.
As for calling ReturnKey, I suppose that does make sense, but the existing code did not do that, and I haven't materially changed that behavior. I'm happy to add it though if I should? (Clarification: My only hesitation is that it looks to me like it's fine to call ReturnKey even if you haven't reserved one, but I'm just not that familiar with the code or it's design, so was hesitant to put in extra calls to ReturnKey where one might not have actually been reserved)
@morcos another PR is fine. As @instagibbs said, this is a bugfix.
2799 | @@ -2773,7 +2800,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT 2800 | } 2801 | } 2802 | 2803 | - if (nChangePosInOut == -1) reserveKey.ReturnKey(); // Return any reserved key if we don't have change 2804 | + if (nChangePosInOut == -1) reservekey.ReturnKey(); // Return any reserved key if we don't have change
In commit "Fix rare edge case of paying too many fees"
This needs to be moved to the previous commit for it to compile.
2761 | + 2762 | + // If we have no change and a big enough excess fee, then 2763 | + // try to construct transaction again only without picking 2764 | + // new inputs. We now know we only need the smaller fee 2765 | + // (because of reduced tx size) and so we should add a 2766 | + // change input. Only try this once.
change output
Wow, this code is ridiculously complicated. Pretty clean fix, though. Would be nice if we could mock SelectCoins and easily write unit tests for these cases.
utACK d64a4b94ea809179e8cc9a92be75036bc8bd4fbf
Fixed commit history and fixed comment nit
git diff d64a4b9 533b3f0
- // change input. Only try this once.
+ // change output. Only try this once.
utACK 533b3f07f0525939d5fef2d22ec1ee699b7d437b
2774 | @@ -2754,6 +2775,12 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT 2775 | } 2776 | break; // Done, enough fee included. 2777 | } 2778 | + else if (!pick_new_inputs) {
Nit: else on the same line as }
utACK 533b3f07f0525939d5fef2d22ec1ee699b7d437b. Changes since last review were what Alex mentioned.
This does not affect behavior but allows us to have access to an output to
scriptChange even if we currently do not have change in the transaction.
Due to the iterative process of selecting new coins in each loop a new fee is
calculated that needs to be met each time. In the typical case if the most
recent iteration of the loop produced a much smaller transaction and we have now
gathered inputs with too many fees, we can just reduce the change. However in
the case where there is no change output, it is possible to end up with a
transaction which drastically overpays fees. This commit addresses that case,
by creating a change output if the overpayment is large enough to support it,
this is accomplished by rerunning the transaction creation loop without
selecting new coins.
Thanks to instagibbs for working on this as well
utACK 0f402b9263b0579b29aa0f841fc64ad58d3efba6. Only difference is rebase.
Posthumous utACK 0f402b9
Milestone
0.15.0