-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet: bugfix, invalid CoinsResult cached total amount #26560
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
wallet: bugfix, invalid CoinsResult cached total amount #26560
Conversation
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. |
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.
Good catch! Unfortunate that this was found right as we're doing the 24.0 release.
0fd9be1
to
f20f88f
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.
oh wow, that's a nasty bug.
Almost Code Review ACK modulo use of available_coins.total_amount
with SFFO in the last commit.
I was quite surprised something like this wasn't caught by some assert. So I dig up and maybe we can add something like e783af3 while you're at it
@@ -112,5 +112,38 @@ BOOST_FIXTURE_TEST_CASE(FillInputToWeightTest, BasicTestingSetup) | |||
// Note: We don't test the next boundary because of memory allocation constraints. | |||
} | |||
|
|||
BOOST_FIXTURE_TEST_CASE(wallet_duplicated_preset_inputs_test, TestChain100Setup) |
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: I'm not sure having second test provides enough extra value. I'd rather keep one, but ultimately up to you.
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.
yeah. I almost remove it but.. ended up leaving because (1) the severity of the bug and (2) because it's easier to debug and assert that the code is failing where is supposed to be failing (in the functional test case, as we use the send
RPC command, we do have other stuff involved).
I'm fine either way, if this still doesn't convince you and more people don't see it useful, can remove it for sure.
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 also agree that the second unit test (which isn't really a unit test) seems redundant and also a little confusing to me. I think the unit test for CoinsResult::Erase
in an earlier commit is correct for a unit test and the functional test is better suited for testing the wallet behavior
Asking here instead of in the backport: is this only in v24 or also earlier versions? |
f20f88f
to
1b14bd7
Compare
I think probably |
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.
The bugfix should also be done in its own commit, absent any refactoring.
I think this means just dropping the break
and replacing the find_if
with remove_if
?
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.
@@ -112,5 +112,38 @@ BOOST_FIXTURE_TEST_CASE(FillInputToWeightTest, BasicTestingSetup) | |||
// Note: We don't test the next boundary because of memory allocation constraints. | |||
} | |||
|
|||
BOOST_FIXTURE_TEST_CASE(wallet_duplicated_preset_inputs_test, TestChain100Setup) |
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 also agree that the second unit test (which isn't really a unit test) seems redundant and also a little confusing to me. I think the unit test for CoinsResult::Erase
in an earlier commit is correct for a unit test and the functional test is better suited for testing the wallet behavior
@luke-jr: we can't end up with the same output twice there. That struct is loaded inside |
…e set The loop break shouldn't have being there.
fwiw, I ran the failing CI test locally and it passed, so I think all you need to do is restart the test |
1b14bd7
to
8f9bd17
Compare
Updated per feedback, thanks everyone. Small diff. Changes:
|
8f9bd17
to
eb76557
Compare
|
ACK eb76557 reviewed the diff, looks good |
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 eb76557
src/wallet/test/spend_tests.cpp
Outdated
coin_control.Select(outpoint); | ||
} | ||
|
||
// First case, use 'subtract_fee_from_outputs' to make the wallet create a tx that pays an insane fee on v24.0. |
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.
Just a nit, but the term "insane fee" had me thinking that this test has us paying a huge amount of fees, something that would be conflicting with our sanity checks.
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.
yeah ok, I used that term because the nFeeRet
calculation prior to the SFFO process, can go insanely high based on the duplicated preset inputs, then we deduct it from the recipient's target.
will improve the documentation.
# Which, combined with the subtract fee from outputs option, | ||
# makes the wallet generate and broadcast a tx that pays | ||
# up to the sum of all the preset inputs, minus one, amounts in | ||
# fee :(. |
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 see, that is the context that I was missing above! :)
|
||
// update cached amounts | ||
total_amount -= coin.txout.nValue; | ||
if (coin.HasEffectiveValue()) total_effective_amount = *total_effective_amount - coin.GetEffectiveValue(); |
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.
Are we sure that total_effective_value
is always set here?
Maybe assume(total_effective_amount.has_value()
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.
I had this question myself. I figured if we remove the coin with effective value, then the coin was added before, hence we have total_effective_value
defined. Please correct me if my reasoning is flawed.
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.
yeah. As we only use CoinsResult::Add
to append new coins: if we have the coin inside CoinsResult
, and the coin has an effective value, then we must have total_effective_amount
.
src/wallet/spend.cpp
Outdated
CAmount available_coins_total_amount = coin_selection_params.m_subtract_fee_outputs ? available_coins.total_amount : | ||
(available_coins.total_effective_amount.has_value() ? *available_coins.total_effective_amount : 0); |
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 will never use SelectCoins()
without a feerate. If you end up not having total_effective_amount here, something went terribly wrong. Perhaps just:
CAmount available_coins_total_amount = coin_selection_params.m_subtract_fee_outputs ? available_coins.total_amount : | |
(available_coins.total_effective_amount.has_value() ? *available_coins.total_effective_amount : 0); | |
assume(available_coins.total_effective_amount.has_value()); | |
CAmount available_coins_total_amount = coin_selection_params.m_subtract_fee_outputs ? available_coins.total_amount : *available_coins.total_effective_amount); |
@@ -452,12 +452,16 @@ void SelectionResult::AddInputs(const std::set<COutput>& inputs, bool subtract_f | |||
|
|||
void SelectionResult::Merge(const SelectionResult& other) | |||
{ | |||
// Obtain the expected selected inputs count after the merge (for now, duplicates are not allowed) |
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.
// Obtain the expected selected inputs count after the merge (for now, duplicates are not allowed) | |
// Store sum of combined input sets to check that no UTXO was used twice |
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 relies on #26560 (comment) (where we are talking about whether should include the assertion commit here or in a follow-up PR).
Please, feel free to comment 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.
strong Concept ACK, I understand the bug has a significant impact and agree with the fix, but the documentation should be changed as I think it is currently incorrect and could be misleading to someone in the future trying to debug / reproduce what was wrong with v24.0.
TLDR I don't think the "insane fee" bug is actually possible? I've made some suggestions for what I think the documentation should be inline (same suggestion wrt the commit messages).
m_target += other.m_target; | ||
m_use_effective |= other.m_use_effective; | ||
if (m_algo == SelectionAlgorithm::MANUAL) { | ||
m_algo = other.m_algo; | ||
} | ||
util::insert(m_selected_inputs, other.m_selected_inputs); | ||
assert(m_selected_inputs.size() == expected_count); |
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 suggest removing this last commit for this PR, and following up with an "add sanity checks to coin selection code" PR.
Of course we don't want to create a bad transaction, but an assert would cause the user's node to crash in case we have implemented it wrong. I think something better could be to add if(!Assume(...)) {return "sorry, something went wrong and transaction creation failed. please report this bug...";}
which should stop the wallet from doing something horrible but also not crash. Apologies if this is bikesheddy - my concrete suggestion is to remove this for now, and do a followup PR for these types of changes. Other appropriate sanity checks could include AddInput()
never adds an input that's already there, SelectCoins
result should cover selection_target
, etc.
|
||
# First case, use 'subtract_fee_from_outputs' to make the wallet create a tx that pays an insane fee on v24.0. | ||
# (further details in #26559). | ||
assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{wallet.getnewaddress(address_type="bech32"): 14}], options=options) |
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.
Cherry-picking this test on top of 24.x, I can see that the wallet mistakenly uses a preset input twice and deducts the fee+5BTC from the payment, but the fee is actually exactly what is expected (since at the end the transaction only has 10BTC inputs and not 15): https://github.com/glozow/bitcoin/blob/test-n26560/test/functional/rpc_fundrawtransaction.py#L1250-L1266
@achow101 suggested that maybe nFeeRet
from FundTransaction()
or CreatedTransactionResult::fee
could be incorrect but I wasn't able to get a test to show that. @achow101 then pointed me to this code showing the sffo fee should be correctly set before CreateTransaction
returns:
Lines 971 to 1004 in f668a3a
// Reduce output values for subtractFeeFromAmount | |
if (coin_selection_params.m_subtract_fee_outputs) { | |
CAmount to_reduce = fee_needed - nFeeRet; | |
int i = 0; | |
bool fFirst = true; | |
for (const auto& recipient : vecSend) | |
{ | |
if (i == nChangePosInOut) { | |
++i; | |
} | |
CTxOut& txout = txNew.vout[i]; | |
if (recipient.fSubtractFeeFromAmount) | |
{ | |
txout.nValue -= to_reduce / outputs_to_subtract_fee_from; // Subtract fee equally from each selected recipient | |
if (fFirst) // first receiver pays the remainder not divisible by output count | |
{ | |
fFirst = false; | |
txout.nValue -= to_reduce % outputs_to_subtract_fee_from; | |
} | |
// Error if this output is reduced to be below dust | |
if (IsDust(txout, wallet.chain().relayDustFee())) { | |
if (txout.nValue < 0) { | |
return util::Error{_("The transaction amount is too small to pay the fee")}; | |
} else { | |
return util::Error{_("The transaction amount is too small to send after the fee has been deducted")}; | |
} | |
} | |
} | |
++i; | |
} | |
nFeeRet = fee_needed; |
I could be mistaken, but in that case could you maybe write a test for v24.0 that shows what the expected vs actual fee are? Otherwise I'd suggest removing this from the comments/commit message/PR descriptions.
Having 2 test cases for sffo and non-sffo codepaths is still meaningful, but I think the current comment is misleading and can cause us to have trouble understanding this test in the future.
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.
Done, updated per feedback. Thanks.
Some context:
The "insane fee" label came from the nFeeRet
calculation. Which, in case of this bug, sets a high value then is later deducted from the recipient's amount. Which.. as we spoke via dm, it's true that this is internal to the wallet only.. so the description should be something like "double-counted preset inputs during Coin Selection incorrectly deduces the user's selected recipient's amount" (or something similar)
Ok great, thanks everyone. Now I see where the confusion arises. |
This exercises the bug inside CoinsResult::Erase that ends up on (1) a wallet crash or (2) a created and broadcasted tx that contains a reduced recipient's amount. This is covered by making the wallet selects the preset inputs twice during the coin selection process. Making the wallet think that the selection process result covers the entire tx target when it does not. It's actually creating a tx that sends more coins than what inputs are covering for. Which, combined with the SFFO option, makes the wallet incorrectly reduce the recipient's amount by the difference between the original target and the wrongly counted inputs. Which means, a created and relayed tx sending less coins to the destination than what the user inputted.
… of direct member access Aside from the cleanup, this solves a bug in the following-up commit. Because, in these tests, we are manually adding/erasing outputs from the CoinsResult object but never updating the internal total amount field.
…target The CoinsResult class will now count the raw total amount and the effective total amount internally (inside the 'CoinsResult::Add' and 'CoinsResult::Erase' methods). So there is no discrepancy between what we add/erase and the total values. (which is what was happening on the coinselector_test because the 'CoinsResult' object is manually created there, and we were not keeping the total amount in sync with the outputs being added/removed).
eb76557
to
7362f8e
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 cf79384 (I assume these 3 are the backport commits) Verified that the tests in cf79384 pass with the fix, fail on 24.x without the fix. I now think the documentation is accurate and would be helpful in a situation in which the test has broken again - thanks for taking the feedback!
ACK 7362f8e, I assume there will be a followup PR to add coin selection sanity checks and we can discuss the best way to do that there.
m_target += other.m_target; | ||
m_use_effective |= other.m_use_effective; | ||
if (m_algo == SelectionAlgorithm::MANUAL) { | ||
m_algo = other.m_algo; | ||
} | ||
util::insert(m_selected_inputs, other.m_selected_inputs); | ||
assert(m_selected_inputs.size() == expected_count); |
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.
Right, so the question of how to implement sanity checks warrants additional discussion and/or refactoring but that discussion shouldn't hold up this PR. Hence the recommendation to remove it.
I disagree that it's harmless to have this assert after the fix; the bug can still exist or be reintroduced. But assuming these last few commits won't be backported and we'll have a followup PR to add/change sanity checks, fine with leaving it in this PR.
ACK 7362f8e |
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 7362f8e
Give the discussion, I think it's best if we drop 3282fad and address it in a follow-up, but not worth holding the PR up if the author feels differently.
A big thanks to furszy for incorporating all of the feedback regarding the tests and documentation: everything is very clear now as to what's being tested and why
m_target += other.m_target; | ||
m_use_effective |= other.m_use_effective; | ||
if (m_algo == SelectionAlgorithm::MANUAL) { | ||
m_algo = other.m_algo; | ||
} | ||
util::insert(m_selected_inputs, other.m_selected_inputs); | ||
assert(m_selected_inputs.size() == expected_count); |
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.
It seems we all (mostly) agree that the assert
is not ideal and how to deal with this should be addressed in a follow-up with more discussion, so I'd recommend dropping the commit so we don't merge code that we know will be removed/refactored shortly after and to avoid any confusion when backporting commits from this PR for v24
… amount 7362f8e refactor: make CoinsResult total amounts members private (furszy) 3282fad wallet: add assert to SelectionResult::Merge for safety (S3RK) c4e3b7d wallet: SelectCoins, return early if wallet's UTXOs cannot cover the target (furszy) cac2725 test: bugfix, coinselector_test, use 'CoinsResult::Erase/Add' instead of direct member access (furszy) cf79384 test: Coin Selection, duplicated preset inputs selection (furszy) 341ba7f test: wallet, coverage for CoinsResult::Erase function (furszy) f930aef wallet: bugfix, 'CoinsResult::Erase' is erasing only one output of the set (furszy) Pull request description: This comes with bitcoin#26559. Solving few bugs inside the wallet's transaction creation process and adding test coverage for them. Plus, making use of the `CoinsResult::total_amount` cached value inside the Coin Selection process to return early if we don't have enough funds to cover the target amount. ### Bugs 1) The `CoinsResult::Erase` method removes only one output from the available coins vector (there is a [loop break](https://github.com/bitcoin/bitcoin/blob/c1061be14a515b0ed4f4d646fcd0378c62e6ded3/src/wallet/spend.cpp#L112) that should have never been there) and not all the preset inputs. Which on master is not a problem, because since [bitcoin#25685](bitcoin#25685) we are no longer using the method. But, it's a bug on v24 (check [bitcoin#26559](bitcoin#26559)). This method it's being fixed and not removed because I'm later using it to solve another bug inside this PR. 2) As we update the total cached amount of the `CoinsResult` object inside `AvailableCoins` and we don't use such function inside the coin selection tests (we manually load up the `CoinsResult` object), there is a discrepancy between the outputs that we add/erase and the total amount cached value. ### Improvements * This makes use of the `CoinsResult` total amount field to early return with an "Insufficient funds" error inside Coin Selection if the tx target amount is greater than the sum of all the wallet available coins plus the preset inputs amounts (we don't need to perform the entire coin selection process if we already know that there aren't enough funds inside our wallet). ### Test Coverage 1) Adds test coverage for the duplicated preset input selection bug that we have in v24. Where the wallet invalidly selects the preset inputs twice during the Coin Selection process. Which ends up with a "good" Coin Selection result that does not cover the total tx target amount. Which, alone, crashes the wallet due an insane fee. But.. to make it worst, adding the subtract fee from output functionality to this mix ends up with the wallet by-passing the "insane" fee assertion, decreasing the output amount to fulfill the insane fee, and.. sadly, broadcasting the tx to the network. 2) Adds test coverage for the `CoinsResult::Erase` method. ------------------------------------ TO DO: * [ ] Update [bitcoin#26559 ](bitcoin#26559) description. ACKs for top commit: achow101: ACK 7362f8e glozow: ACK 7362f8e, I assume there will be a followup PR to add coin selection sanity checks and we can discuss the best way to do that there. josibake: ACK [7362f8e](bitcoin@7362f8e) Tree-SHA512: 37a6828ea10d8d36c8d5873ceede7c8bef72ae4c34bef21721fa9dad83ad6dba93711c3170a26ab6e05bdbc267bb17433da08ccb83b82956d05fb16090328cba
…e set The loop break shouldn't have being there. Github-Pull: bitcoin#26560 Rebased-From: f930aef
Github-Pull: bitcoin#26560 Rebased-From: 341ba7f
This exercises the bug inside CoinsResult::Erase that ends up on (1) a wallet crash or (2) a created and broadcasted tx that contains a reduced recipient's amount. This is covered by making the wallet selects the preset inputs twice during the coin selection process. Making the wallet think that the selection process result covers the entire tx target when it does not. It's actually creating a tx that sends more coins than what inputs are covering for. Which, combined with the SFFO option, makes the wallet incorrectly reduce the recipient's amount by the difference between the original target and the wrongly counted inputs. Which means, a created and relayed tx sending less coins to the destination than what the user inputted. Github-Pull: bitcoin#26560 Rebased-From: cf79384
Backported the 3 commits in #26616. |
8b726bf test: Coin Selection, duplicated preset inputs selection (furszy) 9d73176 test: wallet, coverage for CoinsResult::Erase function (furszy) 195f0df wallet: bugfix, 'CoinsResult::Erase' is erasing only one output of the set (furszy) e5d097b [test] Add p2p_tx_privacy.py (dergoegge) c842670 [net processing] Assume that TxRelay::m_tx_inventory_to_send is empty pre-verack (dergoegge) e15b306 [net processing] Ensure transaction announcements are only queued for fully connected peers (dergoegge) 95fded1 wallet: Explicitly say migratewallet on encrypted wallets is unsupported (Andrew Chow) d464b2a tests: Test for migrating encrypted wallets (Andrew Chow) 7a97a56 wallet: Avoid null pointer deref when cleaning up migratewallet (Andrew Chow) Pull request description: Backports remaining changes on the 24.0.1 milestone. Currently backports: * #26594 * #26569 * #26560 ACKs for top commit: josibake: ACK 8b726bf Tree-SHA512: db77ec1a63a7b6a4412750a0f4c0645681fc346a5df0a7cd38d5d27384e1d0fa95f3953af90042afe131ddbd4b6a6e009527095f13e9f58c0190cd378738a9e5
…error messages 76dc547 gui: create tx, launch error dialog if backend throws runtime_error (furszy) f4d7947 wallet: coin selection, add duplicated inputs checks (furszy) 0aa065b wallet: return accurate error messages from Coin Selection (furszy) 7e8340a wallet: make SelectCoins flow return util::Result (furszy) e5e147f wallet: refactor eight consecutive 'AttemptSelection' calls into a loop (furszy) Pull request description: Work decoupled from #25806, which cleanup and improves the Coin Selection flow further. Adding the capability to propagate specific error messages from the Coin Selection process to the user. Instead of always returning the general "Insufficient funds" message which is not always accurate to what happened internally. Letting us instruct the user how to proceed under certain circumstances. The following error messages were added: 1) If the selection result exceeds the maximum transaction weight, we now will return: -> "The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs". 2) If the user pre-selected inputs and disallowed the automatic coin selection process (no other inputs are allowed), we now will return: -> "The preselected coins total amount does not cover the transaction target. Please allow other inputs to be automatically selected or include more coins manually". 3) The double-counted preset inputs during Coin Selection error will now throw an "internal bug detected" message instead of crashing the node. The essence of this work comes from several comments: 1. bitcoin/bitcoin#26560 (comment) 2. bitcoin/bitcoin#25729 (comment) 3. bitcoin/bitcoin#25269 (review) 4. bitcoin/bitcoin#23144 (which is connected to #24845) ACKs for top commit: ishaanam: crACK 76dc547 achow101: ACK 76dc547 aureleoules: ACK 76dc547 theStack: ACK 76dc547 🌇 Tree-SHA512: 9de30792d7a5849cae77747aa978e70390b66ee9d082779a56088a024f82e725b0af050e6603aece0ac8229f6d73bc471ba97b4ab69dc7eddf419f5f56ae89a5
…ssages 76dc547 gui: create tx, launch error dialog if backend throws runtime_error (furszy) f4d7947 wallet: coin selection, add duplicated inputs checks (furszy) 0aa065b wallet: return accurate error messages from Coin Selection (furszy) 7e8340a wallet: make SelectCoins flow return util::Result (furszy) e5e147f wallet: refactor eight consecutive 'AttemptSelection' calls into a loop (furszy) Pull request description: Work decoupled from bitcoin#25806, which cleanup and improves the Coin Selection flow further. Adding the capability to propagate specific error messages from the Coin Selection process to the user. Instead of always returning the general "Insufficient funds" message which is not always accurate to what happened internally. Letting us instruct the user how to proceed under certain circumstances. The following error messages were added: 1) If the selection result exceeds the maximum transaction weight, we now will return: -> "The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs". 2) If the user pre-selected inputs and disallowed the automatic coin selection process (no other inputs are allowed), we now will return: -> "The preselected coins total amount does not cover the transaction target. Please allow other inputs to be automatically selected or include more coins manually". 3) The double-counted preset inputs during Coin Selection error will now throw an "internal bug detected" message instead of crashing the node. The essence of this work comes from several comments: 1. bitcoin#26560 (comment) 2. bitcoin#25729 (comment) 3. bitcoin#25269 (review) 4. bitcoin#23144 (which is connected to bitcoin#24845) ACKs for top commit: ishaanam: crACK 76dc547 achow101: ACK 76dc547 aureleoules: ACK 76dc547 theStack: ACK 76dc547 🌇 Tree-SHA512: 9de30792d7a5849cae77747aa978e70390b66ee9d082779a56088a024f82e725b0af050e6603aece0ac8229f6d73bc471ba97b4ab69dc7eddf419f5f56ae89a5
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.
Gj
This comes with #26559.
Solving few bugs inside the wallet's transaction creation
process and adding test coverage for them.
Plus, making use of the
CoinsResult::total_amount
cached valueinside the Coin Selection process to return early if we don't have
enough funds to cover the target amount.
Bugs
The
CoinsResult::Erase
method removes only oneoutput from the available coins vector (there is a loop break
that should have never been there) and not all the preset inputs.
Which on master is not a problem, because since #25685
we are no longer using the method. But, it's a bug on v24
(check #26559).
This method it's being fixed and not removed because I'm later using it to solve
another bug inside this PR.
As we update the total cached amount of the
CoinsResult
object insideAvailableCoins
and we don't use such function inside the coin selectiontests (we manually load up the
CoinsResult
object), there is a discrepancybetween the outputs that we add/erase and the total amount cached value.
Improvements
CoinsResult
total amount field to early returnwith an "Insufficient funds" error inside Coin Selection if the tx target
amount is greater than the sum of all the wallet available coins plus the
preset inputs amounts (we don't need to perform the entire coin selection
process if we already know that there aren't enough funds inside our wallet).
Test Coverage
Adds test coverage for the duplicated preset input selection bug that we have in v24.
Where the wallet invalidly selects the preset inputs twice during the Coin Selection
process. Which ends up with a "good" Coin Selection result that does not cover the
total tx target amount. Which, alone, crashes the wallet due an insane fee.
But.. to make it worst, adding the subtract fee from output functionality
to this mix ends up with the wallet by-passing the "insane" fee assertion,
decreasing the output amount to fulfill the insane fee, and.. sadly,
broadcasting the tx to the network.
Adds test coverage for the
CoinsResult::Erase
method.