-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Add change output if necessary to reduce excess fee #10712
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
src/wallet/wallet.cpp
Outdated
// to be addressed so we avoid creating too small an output. | ||
CAmount max_excess_fee = GetMinimumFee(change_prototype_size, currentConfirmationTarget, ::mempool, ::feeEstimator, nullptr) + | ||
GetDustThreshold(change_prototype_txout, ::dustRelayFee); | ||
// If we have no change and a big enough excess fee, then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please move first explanation a few lines before for the "increase change" to appropriate spot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
He moved it, the comment wasn't marked as resolved. Happy with current arrangement.
src/wallet/wallet.cpp
Outdated
@@ -2754,6 +2771,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT | |||
} | |||
break; // Done, enough fee included. | |||
} | |||
else if (!pick_new_inputs) { | |||
// This shouldn't happen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
src/wallet/wallet.cpp
Outdated
@@ -2774,6 +2773,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT | |||
} | |||
} | |||
|
|||
if (nChangePosInOut == -1) reserveKey.ReturnKey(); // Return any reserved key if we don't have change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like a capital "K" snuck in here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird, i fixed that but must have not committed before push
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry to both you again on this but it appears to still be there
src/wallet/wallet.cpp
Outdated
// change input. Only try this once. | ||
if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { | ||
pick_new_inputs = false; | ||
nFeeRet = nFeeNeeded; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, correct
e846170
to
d64a4b9
Compare
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 |
Reference #10247. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, should variables be in camel or snake case? Also could you fix brace styles for the moved code?
@promag 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? |
@morcos another PR is fine. As @instagibbs said, this is a bugfix. |
Replaced #10333 with this one in "High priority for review" (reviewing a closed PR with high priority makes little sense). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 d64a4b9
src/wallet/wallet.cpp
Outdated
@@ -2773,7 +2800,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT | |||
} | |||
} | |||
|
|||
if (nChangePosInOut == -1) reserveKey.ReturnKey(); // Return any reserved key if we don't have change | |||
if (nChangePosInOut == -1) reservekey.ReturnKey(); // Return any reserved key if we don't have change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "Fix rare edge case of paying too many fees"
This needs to be moved to the previous commit for it to compile.
src/wallet/wallet.cpp
Outdated
// try to construct transaction again only without picking | ||
// new inputs. We now know we only need the smaller fee | ||
// (because of reduced tx size) and so we should add a | ||
// change input. Only try this once. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change output
src/wallet/wallet.cpp
Outdated
// to be addressed so we avoid creating too small an output. | ||
CAmount max_excess_fee = GetMinimumFee(change_prototype_size, currentConfirmationTarget, ::mempool, ::feeEstimator, nullptr) + | ||
GetDustThreshold(change_prototype_txout, ::dustRelayFee); | ||
// If we have no change and a big enough excess fee, then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
d64a4b9
to
533b3f0
Compare
Fixed commit history and fixed comment nit
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 533b3f0
@@ -2754,6 +2775,12 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT | |||
} | |||
break; // Done, enough fee included. | |||
} | |||
else if (!pick_new_inputs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: else on the same line as }
utACK 533b3f0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 533b3f0. 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.
…ange. 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
533b3f0
to
0f402b9
Compare
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: 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 Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 0f402b9. Only difference is rebase.
Posthumous utACK 0f402b9 |
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: This is an alternative to bitcoin#10333 See commit messages. The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8 Signed-off-by: Pasta <pasta@dashboost.org>
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: This is an alternative to bitcoin#10333 See commit messages. The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8 Signed-off-by: Pasta <pasta@dashboost.org>
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: This is an alternative to bitcoin#10333 See commit messages. The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8 Signed-off-by: Pasta <pasta@dashboost.org>
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: This is an alternative to bitcoin#10333 See commit messages. The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8 Signed-off-by: Pasta <pasta@dashboost.org>
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: This is an alternative to bitcoin#10333 See commit messages. The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8 Signed-off-by: Pasta <pasta@dashboost.org>
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: This is an alternative to bitcoin#10333 See commit messages. The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8 Signed-off-by: Pasta <pasta@dashboost.org>
0f402b9 Fix rare edge case of paying too many fees when transaction has no change. (Alex Morcos) 253cd7e Only reserve key for scriptChange once in CreateTransaction (Alex Morcos) Pull request description: This is an alternative to bitcoin#10333 See commit messages. The first commit is mostly code move, it just moves the change creation code out of the loop. @instagibbs Tree-SHA512: f16287ae0f0c6f09cf8b1f0db5880bb567ffa74a50898e3d1ef549ba592c6309ae1a9b251739f63a8bb622d48f03ce2dff9e7a57a6bac4afb4b95b0a86613ea8 Signed-off-by: Pasta <pasta@dashboost.org>
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