Skip to content

Conversation

aureleoules
Copy link
Contributor

@aureleoules aureleoules commented Jul 28, 2022

This PR is an attempt to fix #5782.

I have added 4 test scenarios, 3 of them provided here #5782 (comment) (slightly modified to use a segwit wallet).

Here are my benchmarks :

PR

ns/op op/s err% ins/op cyc/op IPC bra/op miss% total benchmark
1,466,341.00 681.97 0.6% 11,176,762.00 3,358,752.00 3.328 1,897,839.00 0.3% 0.02 CoinSelection

Master

ns/op op/s err% ins/op cyc/op IPC bra/op miss% total benchmark
1,526,029.00 655.30 0.5% 11,142,188.00 3,499,200.00 3.184 1,994,156.00 0.2% 0.02 CoinSelection

@aureleoules aureleoules marked this pull request as draft July 28, 2022 13:34
@aureleoules aureleoules force-pushed the 2022-07-coin-selection-check-max-weight branch 5 times, most recently from 5123bd5 to 45b5a31 Compare July 28, 2022 15:37
@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 28, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK achow101, w0xlt, furszy
Stale ACK Xekyo

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #25806 (wallet: group outputs only once, decouple it from Coin Selection by furszy)

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.

@achow101
Copy link
Member

It shouldn't be necessary to pass an entire CMutableTransaction to SelectCoins. CoinSelectionParams already has the information about the sizes of the rest of the transaction, modulo the change output. So a calculation can be done with that information.

Additionally, depending on the algorithm used, there may or may not be a change output, so it's size will need to be accounted for in each size calculation.

@aureleoules
Copy link
Contributor Author

aureleoules commented Jul 28, 2022

Thank you for the feedback @achow101.
Sizes in the CoinSelectionParams appear to be in "bytes" and not in "weight", so I'm not sure how to use them as the multiplier of each field hasn't been taken into account, unless I'm mistaken.

Additionally, depending on the algorithm used, there may or may not be a change output, so it's size will need to be accounted for in each size calculation.

I'll look into that thanks.

@achow101
Copy link
Member

The size is always vsize, so multiplyging by WITNESS_SCALE_FACTOR is sufficient to convert to weight. There will be some rounding error, but vsize always rounds up, so weight calculated in that way will always be a slight overestimate.

@aureleoules
Copy link
Contributor Author

I've removed the need for a dummy transaction.
Though I'm not sure how to include the change output in the size calculation yet.

@aureleoules aureleoules force-pushed the 2022-07-coin-selection-check-max-weight branch 6 times, most recently from e06a41f to 0741aaa Compare July 28, 2022 23:33
@aureleoules aureleoules marked this pull request as draft July 29, 2022 13:11
@aureleoules aureleoules marked this pull request as ready for review July 30, 2022 09:36
@aureleoules
Copy link
Contributor Author

aureleoules commented Aug 3, 2022

Pinging you @xekyo as you commented on the issue recently.

@achow101
Copy link
Member

achow101 commented Nov 18, 2022

No, it is correct as it is now. Converting vbytes to weight is always just multiply by WITNESS_SCALE_FACTOR. It doesn't matter what the script type is.

@aureleoules
Copy link
Contributor Author

Okay great thank you!

@aureleoules aureleoules force-pushed the 2022-07-coin-selection-check-max-weight branch 2 times, most recently from 962bb57 to 30b71d1 Compare November 19, 2022 23:36
Copy link
Member

@furszy furszy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code review ACK 30b71d1, left few small comments.

@achow101
Copy link
Member

ACK 30b71d1

aureleoules and others added 2 commits December 5, 2022 19:32
Co-authored-by: Andrew Chow <github@achow101.com>
Co-authored-by: Andrew Chow <github@achow101.com>
@aureleoules aureleoules force-pushed the 2022-07-coin-selection-check-max-weight branch from 30b71d1 to c7c7ee9 Compare December 5, 2022 18:32
@aureleoules
Copy link
Contributor Author

Rebased

@DrahtBot DrahtBot changed the title wallet: Check max transaction weight in CoinSelection wallet: Check max transaction weight in CoinSelection Dec 5, 2022
@achow101
Copy link
Member

achow101 commented Dec 5, 2022

reACK c7c7ee9

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK c7c7ee9

@@ -354,6 +355,10 @@ void OutputGroup::Insert(const COutput& output, size_t ancestors, size_t descend
// descendants is the count as seen from the top ancestor, not the descendants as seen from the
// coin itself; thus, this value is counted as the max, not the sum
m_descendants = std::max(m_descendants, descendants);

if (output.input_bytes > 0) {
m_weight += output.input_bytes * WITNESS_SCALE_FACTOR;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is fine for the purposes of this PR, the weight here is not always precise due to rounding error of input_bytes.

Ideally, we would store the actual weight in COutput and just take it from there. I realise, this might be out of scope for this PR. But maybe we can add a warning in code for future devs and create an issue in this repo?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the weight here is not always precise due to rounding error of input_bytes

Could elaborate, since both COutput::input_bytes and WITNESS_SCALE_FACTOR are integer ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 virtual byte = 4 weight units. When computing vbytes from weight, if the weight is not divisible by 4, the vbytes is rounded up. For example, 6 WU is really 1.5 vbytes, but is rounded to 2 vbytes. Since input_bytes stores this rounded number, computing weight from it results in 8 WU rather than the correct value of 6.

Copy link
Member

@furszy furszy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff ACK c7c7ee9

@@ -888,7 +897,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
}

// Include the fees for things that aren't inputs, excluding the change output
const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.m_subtract_fee_outputs ? 0 : coin_selection_params.tx_noinputs_size);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could:

Suggested change
const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.m_subtract_fee_outputs ? 0 : coin_selection_params.tx_noinputs_size);
const CAmount not_input_fees = coin_selection_params.m_subtract_fee_outputs ? 0 : coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually rather that not_input_fees is always calculated, and the SFFO handling is done for just the target. We really need to try to reduce the number of places that we have differences between normal and SFFO coin selection. Since not_input_fees is used elsewhere, having its value be dependent on SFFO results in a cascading effect that I think we should be avoiding.

But that can be done in a followup.

@achow101 achow101 merged commit ef744c0 into bitcoin:master Dec 6, 2022
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Dec 6, 2022
achow101 added a commit to bitcoin-core/gui that referenced this pull request Jan 3, 2023
…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
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 4, 2023
…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
@aureleoules aureleoules deleted the 2022-07-coin-selection-check-max-weight branch January 12, 2023 11:51
achow101 added a commit to bitcoin-core/gui that referenced this pull request Apr 20, 2023
…ults that exceed the max allowed weight

25ab147 refactor: coinselector_tests, unify wallet creation code (furszy)
ba9431c test: coverage for bnb max weight (furszy)
5a2bc45 wallet: clean post coin selection max weight filter (furszy)
2d11258 coin selection: BnB, don't return selection if exceeds max allowed tx weight (furszy)
d3a1c09 test: coin selection, add coverage for SRD (furszy)
9d9689e coin selection: heap-ify SRD, don't return selection if exceeds max tx weight (furszy)
6107ec2 coin selection: knapsack, select closest UTXO above target if result exceeds max tx size (furszy)
1284223 wallet: refactor coin selection algos to return util::Result (furszy)

Pull request description:

  Coming from the following comment bitcoin/bitcoin#25729 (comment).

  The reason why we are adding hundreds of UTXO from different sources when the target
  amount is covered only by one of them is because only SRD returns a usable result.

  Context:
  In the test, we create 1515 UTXOs with 0.033 BTC each, and 1 UTXO with 50 BTC. Then
  perform Coin Selection to fund 49.5 BTC.

  As the selection of the 1515 small UTXOs exceeds the max allowed tx size, the
  expectation here is to receive a selection result that only contain the big UTXO.
  Which is not happening for the following reason:

  Knapsack returns a result that exceeds the max allowed transaction size, when
  it should return the closest utxo above the target, so we fallback to SRD who
  selects coins randomly up until the target is met. So we end up with a selection
  result with lot more coins than what is needed.

ACKs for top commit:
  S3RK:
    ACK 25ab147
  achow101:
    ACK 25ab147
  Xekyo:
    reACK 25ab147
  theStack:
    Code-review ACK 25ab147

Tree-SHA512: 2425de4cc479b4db999b3b2e02eb522a2130a06379cca0418672a51c4076971a1d427191173820db76a0f85a8edfff100114e1c38fb3b5dc51598d07cabe1a60
@bitcoin bitcoin locked and limited conversation to collaborators Jan 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Coin selection, CreateTransaction, MAX_STANDARD_TX_SIZE
8 participants