-
Notifications
You must be signed in to change notification settings - Fork 37.7k
bumpfee: Allow the user to choose which output is change #26467
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
bumpfee: Allow the user to choose which output is change #26467
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.
We support subtracting fee from output, so I think the "reduce_output"
name used in #12096 was better. (Would be nice if that situation could be detected, but I don't think we save the metadata needed for that.)
Also, might make sense to split the GUI part off to the GUI repo after the rest is merged. Seems like a hard thing to get a good UX for.
527b89d
to
6d10ece
Compare
Concept ACK |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
6d10ece
to
73d4ba2
Compare
Concept ACK. Tested and code reviewed 73d4ba2. Suggest changing the dialog UI to support scrolling and resizing in case there are lots of outputs: Here is a drop-in gist to implement scrolling and resizing, which changes it to: If a user creates a single-output tx and chooses that output as "change" to fee-bump, it will fail with the "Transaction must have at least one recipient" message. This makes sense if the user always expects the "payment" output to remain the same value, but if they're consolidating UTXOs or something, this may be desirable to allow. |
Agreed. The help text could clarify that in the general case you should pick the change address output for this. |
bf222ab
to
ceab670
Compare
I've taken the name, but I'm not entirely sure that it is accurate. While we will most likely reduce the amount of the specified output, it's not the only outcome. It could be that specified output does not provide enough value, in which case we will add additional inputs and set the output's value as the resulting change value, which will be higher than the original value.
Done, moved the GUI commits to bitcoin-core/gui#700
Took this suggestion in the PR to the GUI repo.
Added a commit which handles that case. |
Concept ACK Did you mean for the middle commit to have On signet with an empty mempool, when I send a transaction with a single input at 1 sat/vbyte and then try to bump it to 2 sat/byte, it complains:
Maybe |
ceab670
to
0b280af
Compare
Nope, fixed.
I'm going to say that this is an orthogonal issue. It looks like the wallet has a minimum incremental feerate of 5 sat/vb, rather than the 1 sat/vb the mempool uses. |
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.
tACK 0b280af
src/wallet/feebumper.cpp
Outdated
|
||
return Result::OK; | ||
return Result::OK; |
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.
0b280af nit: return
could be outside the if else
. If you touch it, there's also a typo in the commit description: transcation
0b280af
to
4167843
Compare
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.
ACK 4167843
reviewed code, ran tests. Played with the feature on regtest and in particular using reduce_output
to intentionally reduce the recipient output instead of the original change output added automatically by sendtoaddress
. I don't think is explicitly covered in the test but could be. One other quesiton about the test below but otherwise LGTM
Show Signature
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
ACK 4167843e0729994954317e2a4ac8a96a453bad79
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmP4544ACgkQ5+KYS2KJ
yTr/hw//RCFzObS/NFeiJjG4Btb6kuDMDW7SYmXaEazT0mX91QpiQlq+cW92/FZa
FDqycJs43hMMu0ytmTJ8qs9S0sROU0IYRJghK16Ne16TBhtJwbpS93NIQo4LQY7j
a77Ql9DmDGt/c5EkN+lQsvk6W6PXlo1NqwvXT5Rn3NXnbJ0yD4PCo5ZQW1GQ305i
6pGboMlx97PGRtnCsFvfO6+lOAUlAPiwYXt8a+yfT2jhhFzmoi0KG4NlcPLHmwCL
OPKken36AE/2WMpZLu12nDMAb6qM3ia7/GiKxEB6+C0j0RBxYF4RAjmKxgNo/XJ/
f2yZiPmDSsGnZWaYRMpOjR5s0HgQS98WRUK17ZJfz8tN7qR9Y8L8wUwLTpvqHM/a
i+Je0mXY1k5iUtkOG9D9PKAAmsQ6OQzoCW1vzuKL1Z1jc2nKZHbuYbuE8OzyKlGc
A4gv/pNSyaWLHP7TqwvzxHTVlCNlbp+bN+PB21ojd4xsItCU5AGbLAxAMF+LJnwO
YL5xI+29voZcnZBiQI9/jHV8VJ5mUYYDT4f/nQYkTlMmAcBx7BzPF4Zh3h7gKOBA
ybhrHHsSNiXVcm2VIqI6Zb/Wvn/zRTJscxvRQNDuYvEGeCMjQSc1uaGulFflWLmq
qtGtR6U4j4GRTL8TyQcAZu4VfuaUH3up2Km3cKAAFvNqbaKOqlo=
=A42I
-----END PGP SIGNATURE-----
pinheadmz's public key is on keybase
|
||
new_tx = rbf_node.gettransaction(txid=new_txid, verbose=True) | ||
assert_equal(len(new_tx["decoded"]["vout"]), 2) | ||
new_change_pos = find_vout_for_address(rbf_node, new_txid, change_addr) |
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.
Will new_change_pos == change_pos
always here? Or are the outputs of the replacement re-randomized?
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.
They will be re-randomized.
Concept ACK |
ec3b93d
to
dff7235
Compare
dff7235
to
2eeebc5
Compare
If the user used a custom change address, it may not be detected as a change output, resulting in an additional change output being added to the bumped transaction. We can avoid this issue by allowing the user to specify the position of the change output.
2eeebc5
to
e8c31f1
Compare
re ACK e8c31f1 |
Not seeing this in this branch. |
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.
re-ACK e8c31f1
Reviewed all code changes, ran tests locally. I played with the feature in regtest with wallet TXs as well as PSBTs. Made sure invalid input threw errors and expected output was reduced by fee bump. Found one nit below. Maybe remove GUI from OP description? Is that being added in a bitcoin-core/gui PR ?
Show Signature
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
ACK e8c31f135c6e9a5f57325dbf4feceafd384f7762
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmS1V/kACgkQ5+KYS2KJ
yTon4g/+JpVTIdCwb4fvLOiulKLu+poVhET+Wz9iyC+47m4GJUPgcR8RyEcPFa+6
eEn7V1md7o9pOekPQnBLStZJZSFQwuJgG0utgt/M0a5yzQlNNsTcEh2nWqSAI1X2
RAzxOlBjomFzQndm8zQ6QVQRX4tmrwKdnJxumZxWrnRBToJCvikdDVzHIP1yS0CE
d9Z2yWNgJKkocuWACMOgfjt5GjR/Y0Y4qPDahcI10qpWjD2yFJMrx5DUjUnMxXyD
H/3+kYUga9yfFAlE1J9OyCWpL1k//zRcttBZxDoHZ/Hh3MIMvcla6eRAjndd0iU0
o92BVZS1+Ylz4mY2+aoja1yoXQa9JJwJavyOVNR0HtzXXtkzkWdf8shMcUe7jTXb
wlUJwm4+ItIZ05ms05eTmcxfDOc6aQjKud26hIG0TaztfLAZU3ITDdU9lHeZB8qC
szHpyVxLJ63BtaQCOdC//asAqxsn44EZlM3LY9iUE1jvxLAaaHN7yPgSO62A1B3N
RbQ+5BYLYWv6ceAnyUKxX7XnzEGjsFQnSe45KVYZ42zy4eO6kuc/xKy7his3oJ9r
7JT1FgA41wY8mi4KazB6JRyTwhcx0QQ3UxwbnLy3tzsTzWw0aU3dPqwG7iR2dTqH
Ac08JiqHzUCtTiYbRVrrATkgoZNeBqjCRhVr3o0z+2iKZLm6lWg=
=8/+K
-----END PGP SIGNATURE-----
pinheadmz's public key is on keybase
} else { | ||
for (size_t i = 0; i < txouts.size(); ++i) { | ||
const CTxOut& output = txouts.at(i); | ||
if (reduce_output.has_value() ? reduce_output.value() == i : OutputIsChange(wallet, 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.
nit: extra space?
if (reduce_output.has_value() ? reduce_output.value() == i : OutputIsChange(wallet, output)) { | |
if (reduce_output.has_value() ? reduce_output.value() == i : OutputIsChange(wallet, output)) { |
That was moved to bitcoin-core/gui#700. Dropped that from the PR description. |
for (size_t i = 0; i < txouts.size(); ++i) { | ||
const CTxOut& output = txouts.at(i); | ||
if (reduce_output.has_value() ? reduce_output.value() == i : OutputIsChange(wallet, output)) { | ||
CTxDestination change_dest; | ||
ExtractDestination(output.scriptPubKey, change_dest); | ||
new_coin_control.destChange = change_dest; | ||
} else { |
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.
If the transaction has two outputs: index 0 not change, and index 1 change for the wallet. When reduce_output=0
, the first round of the loop will set destChange
to the first output script, then the second round will overwrite destChange
with the second output script.
This will have the bad outcome of dropping the first output (the one selected by the user) and send all coins to the second one. Leaving the bumped tx with only one output.
A solution for this could be to not discard change outputs from the recipients vector. But, for that, we need #27601. Which basically enables this feature.
So instead of having this feebumper loop dropping the change output, we can append it to the recipients list and specify inside coin control that we want to send any surplus between inputs and outputs there.
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, this comment was posted yesterday but is dated over a month ago (Jun 9). I thought for sure I had tested this case locally and didn't see a missing output -- is this still the case here? Since we check reduce_output.has_value()
in the if condition, doesn't that mean that if that option is set only the selected option will be designated as 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.
I thought for sure I had tested this case locally and didn't see a missing output -- is this still the case here? Since we check
reduce_output.has_value()
in the if condition, doesn't that mean that if that option is set only the selected option will be designated as change?
Yeah. Seems that I was partially blind yesterday. This issue can still be triggered by a transaction with +2 change outputs and no reduce_output
(OutputIsChange()
will be true for them, which overwrites destChange
). But.. it isn't related to this PR.
So.. nvm. Will do another review round. Thanks @pinheadmz.
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.
Code review ACK e8c31f1
OutputsDoc(), | ||
RPCArgOptions{.skip_type_check = true}}, | ||
{"reduce_output", RPCArg::Type::NUM, RPCArg::DefaultHint{"not set, detect change automatically"}, "The 0-based index of the output from which the additional fees will be deducted. In general, this should be the position of change output. Cannot be provided if 'outputs' is specified."}, |
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 7d83502:
The first sentence "The 0-based index of the output from which the additional fees will be deducted"
isn't totally accurate to what will happen.
If the bumpfee
process adds another input to satisfy the new fee, the reduce_output
output will also be increased by the new inputs - outputs
surplus.
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.
@furszy makes an important point. Consider the case where I am paying someone on chain under the stipulation that they will eat the mining fee. (Thus, I specify their address in subtractfeefrom
when I'm constructing my transaction.) Later, they tell me that they can't wait for confirmation any longer and need an urgent fee bump. If I naïvely specify their output as the reduce_output
, I may end up paying them much more than I intended to if I only have large UTxOs in my wallet.
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.
@whitslack yes. That would be an ugly outcome. We could have fixed the RPC docs here.
Would say to work towards #27601 to fix this scenario (and others) properly.
This issue comes from a workaround code that avoids the dup change outputs bug. Basically, the change output is manually discarded from the recipients list and expected to be re-added later, with the new fees subtracted, by the inner transaction creation process logic.
With #27601, we will be able to specify the output to reduce (SFFO) and the existent change output unequivocally.
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 seems like a very serious bug that should have blocked the merge... :|
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.
Should we disable the adding of new inputs when reduce_output
is set? If someone really intends to do that, they should probably use outputs
.
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.
Made a separate issue to track this: #28180
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.
Very good
When bumping the transaction fee, we will try to find the change output of the transaction in order to have an output whose value we can modify so that we can meet the target feerate. However we do not always find the change output which can cause us to unnecessarily add an additional output to the transaction. We can avoid this issue by outsourcing the determination of change to the user if they so desire.
This PR adds a
orig_change_pos
option to bumpfee which the user can use to specify the index of the change output.Fixes #11233
Fixes #20795