Skip to content

Conversation

@yancyribbens
Copy link
Contributor

This is a small nit, however I think it's more understandable to write:

utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee

vs

(utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0

@AryanJ-NYC
Copy link

Concept ACK, Approach ACK

@DrahtBot
Copy link
Contributor

DrahtBot commented Sep 13, 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 Xekyo, aureleoules, achow101
Approach ACK AryanJ-NYC

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

Conflicts

No conflicts as of last run.

Copy link
Contributor

Choose a reason for hiding this comment

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

ACK 1a2d94ac586e2ecb55994205fceec14322a949fd

Since the feerate is loop invariant, that boolean will always evaluate the same across one coin selection attempt, and it would be even better to just pull it out of the loop:

Suggested change
(curr_waste > best_waste && (utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee))) { // Don't select things which we know will be more wasteful if the waste is increasing
[before loop]
bool is_high_feerate = utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee;
[…]
(is_high_feerate && curr_waste > best_waste)) { // Don't select things which we know will be more wasteful if the waste is increasing

Copy link
Contributor Author

@yancyribbens yancyribbens Sep 16, 2022

Choose a reason for hiding this comment

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

I considered this as well. To my surprise moving the comparison outside of the loop didn't make any benchmark difference. My guess is the compiler must be smart enough to do some optimization, or the benchmark tests aren't sensitive enough to detect a change.

That bieng said, I do think it's more readable outside of the for loop (and doesn't need to be compared every iteration). @xekyo does this look better 8ada8bb?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe this is better as two separate commits. 365aca4 and 953ce06

Copy link
Contributor

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

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

Yeah, looks better outside of the loop, but I think the comment would be better as an amendment of the big description in the function, but I also disagree with how the algorithm is characterized.

Comment on lines 90 to 92
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not a good description of what the algorithm does. BnB deterministically searches the combination tree of the UTXO pool and retains the input set with the minimal waste score among the input sets it traverses. It doesn't guarantee to find the minimal waste score, and it doesn't guarantee to minimize the input set at high feerates. The result may end up being the minimal input set at high fees but might not be. E.g. if the feerate were 10.001 sat/vB and the LTFRE 10 sat/vB, the waste score could prefer a signficantly larger input set that results in a smaller excess over a smaller one with large excess, even while this bool classify the coin selection situation as is_feerate_expensive = True.

Meanwhile at low feerates BnB does not guarantee to maximimize the input set—it also keeps the input set with lowest waste score. Since the depth-first search starts with the most valuable UTXOs, input sets with a smaller count are traversed before ones with larger count are visited. Even then, the UTXO types may cause a smaller count to be preferred because the total weight of older output types could be bigger than a smaller count of modern outputs. Depending on the UTXO pool size, it may very well not find the maximal input set within the 100,000 tries:
it doesn't take many UTXOs for the 100,000 tries to get exhausted before all relevant combinations are visited. Depending on the variance of the present values some 400+ UTXOs can suffice already, but wallets may have thousands or even tens of thousands of UTXOs.

If you insist on adding a comment perhaps use something along the lines of:

Suggested change
// Check if the current feerate is expensive. If it is expensive, we minimize the size of
// the result set and thus minimizing the transaction fee. If the fees are not expensive,
// we maximize the result set to reduce the amount of UTXOs (fragmentation).
// Check if the current feerate is higher than the long-term feerate estimate.
// Since BnB prefers the input set with the lowest waste score, this results in a preference
// for smaller input sets at high feerates and bigger input sets at lower feerates.

You might want to consider adding this to the comment on the function though, as that point is not well-covered there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My comment was misleading it looks like. It's true that a true maximum or true min may not be found before the iteration limit. Also, I appreciate you pointing out that how many or how few UTXO's are targeted can depend on how much the longterm and short term fees differ. Perhaps we can just do without a comment here since the statement is self explanatory. I agree that further comment on how the selection process works is better to place on the function.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think "expensive" is the best term to refer to a feerate. Creating a transaction at a high feerate is expensive, but the feerate itself doesn't cost anything. Perhaps:

Suggested change
bool is_feerate_expensive = utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee;
bool is_feerate_high = utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My thought process was simply that we are checking if the current fee rate is expensive compared to the longterm fee rate. The reason I like expensive is it's a monetary term and "high" can pertain to many things. Since you feel strongly about using "high" I'll change it to your suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

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

A non-representative survey among my colleagues resulted in three out of three (including one native English speaker with a finance background) agreeing with me: a transaction is expensive when its feerate is high.

I therefore maintain that feerates are “high” or “low”, not “expensive” or “cheap”.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It occurred to me that in the study of computation, expensive is sometimes used to describe a computation that takes a lot of resource, therefore it's not purely a finical term. Also, since we are terming the transaction as expensive, it does make sense to term the feerate as high.

@yancyribbens
Copy link
Contributor Author

yancyribbens commented Sep 17, 2022

@xekyo does this look better? 81d4a2b

@murchandamus
Copy link
Contributor

ACK 81d4a2b

@yancyribbens
Copy link
Contributor Author

@achow101 friendly request to take a look. It's a small change but I don't see a reason not to include it.

Copy link
Contributor

@aureleoules aureleoules left a comment

Choose a reason for hiding this comment

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

ACK 81d4a2b

@achow101
Copy link
Member

achow101 commented Jan 3, 2023

ACK 81d4a2b

achow101 added a commit to bitcoin-core/gui that referenced this pull request Jan 3, 2023
81d4a2b refactor: Move feerate comparison invariant outside of the loop (yancy)
365aca4 refactor: Simplify feerate comparison statement (yancy)

Pull request description:

  This is a small nit, however I think it's more understandable to write:

  `utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee`

  vs

  `(utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0`

ACKs for top commit:
  Xekyo:
    ACK 81d4a2b
  achow101:
    ACK 81d4a2b
  aureleoules:
    ACK 81d4a2b

Tree-SHA512: 3e89377989c36716b53114fe40178261671dde5688075fab1c21ec173ac310f8c84ed6af90354d7c329176cb7262dfcaa7191fd19847d3b7147a9a10c3e31176
@achow101
Copy link
Member

achow101 commented Jan 3, 2023

This was merged.

@achow101 achow101 closed this Jan 3, 2023
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 4, 2023
81d4a2b refactor: Move feerate comparison invariant outside of the loop (yancy)
365aca4 refactor: Simplify feerate comparison statement (yancy)

Pull request description:

  This is a small nit, however I think it's more understandable to write:

  `utxo_pool.at(0).fee > utxo_pool.at(0).long_term_fee`

  vs

  `(utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0`

ACKs for top commit:
  Xekyo:
    ACK 81d4a2b
  achow101:
    ACK 81d4a2b
  aureleoules:
    ACK 81d4a2b

Tree-SHA512: 3e89377989c36716b53114fe40178261671dde5688075fab1c21ec173ac310f8c84ed6af90354d7c329176cb7262dfcaa7191fd19847d3b7147a9a10c3e31176
@bitcoin bitcoin locked and limited conversation to collaborators Jan 3, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants