Skip to content
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

Add Single Random Draw as an additional coin selection algorithm #17526

Merged
merged 7 commits into from
Sep 28, 2021

Conversation

achow101
Copy link
Member

@achow101 achow101 commented Nov 19, 2019

To ease in the use of SRD as our fallback mechanism, this PR adds it as a secondary fallback algorithm in addition to the knapsack solver. Since #22009, the solution with the least waste will be chosen. This pattern is continued with SRD simply being another solution whose waste is compared.

@achow101
Copy link
Member Author

Some simulations results: https://gist.github.com/achow101/edf6b5e308035a489fbb1f28d12e2109

I think the important thing to see here is that this is the same or better in every metric except for "mean UTXOs". But I think the thing to note here is that it's about the same mean UTXOs as the positive only effective value simulation. So I think the different there is primarily due to dust outputs that aren't being consumed.

@DrahtBot
Copy link
Contributor

DrahtBot commented Nov 20, 2019

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #17211 (Allow fundrawtransaction and walletcreatefundedpsbt to take external inputs by achow101)

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.

@laanwj laanwj added the Feature label Mar 27, 2020
@murchandamus
Copy link
Contributor

murchandamus commented Sep 6, 2021

Any simulations/results/intuitions on how often each of the algorithms are likely to be picked?

Not currently. Perhaps @xekyo has something?

That depends heavily on the amount and value diversity of the UTXOs that are available. I've seen an exchange wallet do 93% BnB transactions across a timeframe of four weeks just after enabling BnB while they were chewing through a large UTXO pool. The same wallet later settled around 55-65%, IIRC. I'd guess most enterprises with a send-and-receive hot wallet would probably fall into the range of 30-60%, send-only enterprise wallets probably more like 5-15%. On the higher end of that range if they're batching payments since BnB solutions are easier to find for larger amounts.

On an end-user wallet I would expect 0-15% BnB solutions, especially on the lower end if it often sends at extremely low feerates since that shrinks the exact match window (as the cost of change is less) and tends to consolidate the available UTXO pool further.

For some of the tests within rpc_fundrawtx, there is the expectation
that two independent calls to coin selection RPCs will use the same type
of UTXO. This is not necessarily guaranteed, so to make sure it is, use
lockunspent prior to those tests.
For the test that checks that there is no error when change is
unavailable but change is also not needed, use specific UTXOs so that
SRD does not cause this to fail when it chooses random inputs.
@murchandamus
Copy link
Contributor

since I believe the argument is change-less solutions are simply only preferred after this in an economic sense, not privacy/ending the chaining of change way.

Given that the change output has a big negative impact on the waste score, the BnB solution should always be preferred, if it has the same count of inputs (given the same type). Due to usually needing to combine multiple inputs in order to arrive at a specific amount and the deterministic search path going from highest effective value to lowest, BnB solutions tend to have a small count of 2 or more inputs.

Especially in a cluttered wallet, SRD or Knapsack often find a larger solution that may be preferred at low feerates even when it produces a change output. At higher feerates, especially SRD can find the occasional lucky single input solutions that would might have a lower waste score. This may result in slightly fewer BnB solutions than before, but only in cases where the BnB solution was less cost effective.

Given the high estimated cost of change outputs, I'd say that we already have a decent handicap on solutions producing change. It would be possible to further dissuade change, but I'd consider it an open question how to pick the "correct" handicap.

@achow101
Copy link
Member Author

I've ran simulations (using the same data) on a recent commit of master as well as on this PR. I've uploaded the results to a gist for master and for this PR.

The main result from this particular simulation is that with SRD, less inputs are being consumed overall (mean # of utxos is higher), but fees are slightly lower. SRD ended up being used ~1/3 of the time, BnB ~1/5, and Knapsack the rest.

@murchandamus
Copy link
Contributor

I've ran simulations (using the same data) on a recent commit of master as well as on this PR. I've uploaded the results to a gist for master and for this PR.

The main result from this particular simulation is that with SRD, less inputs are being consumed overall (mean # of utxos is higher), but fees are slightly lower. SRD ended up being used ~1/3 of the time, BnB ~1/5, and Knapsack the rest.

Let me try to cast the results slightly differently: the variant with SRD reduces fees by 6% and while that variant spends 296 fewer inputs those seem to be largely accounted for by it creating 255 fewer change outputs in the first place. So yes, the mean UTXO pool is a bit larger, but in effect it also creates 4.4% fewer change outputs in the first place. Not creating so many UTXOs in the first place seems great to me!

@achow101
Copy link
Member Author

I redid the simulations with some fixes to how data is gathered and gathered a lot more data. The original simulation was accidentally incorrectly counting usage due to the double selection that occurs for the optimistic avoidpartialspends. I am now also measuing how many uneconomical UTXOs are being spent, and how many changeless txs of each algorithm produces. The aforementioned gist has been updated with the new measurements.

With the new measuring, we see that SRD produces a lot more BnB solutions and ends up spending almost no uneconomical outputs (which would explain the significant increase in UTXOs in the wallet).

Copy link
Member

@glozow glozow 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 981b9d1

I wanted to see if there were any other tests that could be broken due to relying on deterministic coin selection behavior, so I edited AttemptSelection to always return the SRD solution; all the functional tests passed.

src/wallet/coinselection.h Outdated Show resolved Hide resolved
test/functional/rpc_fundrawtransaction.py Show resolved Hide resolved
test/functional/rpc_fundrawtransaction.py Outdated Show resolved Hide resolved
test/functional/rpc_fundrawtransaction.py Outdated Show resolved Hide resolved
Don't assume that specific inputs are going to be used when they aren't
specified explicitly.

Also fixes a bug in the include_unsafe test where after the inputs
confirm, include_unsafe should be set to False rather than True.
Instead of relying on coin selection to deterministically choose
the correct inputs to use, just specify them explicitly and use
the raw transaction RPCs.
To avoid accidentally spending UTXOs that are needed later in the test,
lock those UTXOs after they're creation.
Try to find a solution with SelectCoinsSRD. If we do have one, add it to
the list of solutions from which we choose the one with the least waste
as the solution to use.
@glozow
Copy link
Member

glozow commented Sep 24, 2021

reACK 3633b66 via git range-diff 981b9d1...3633b66, thanks for taking the suggestions

Copy link
Contributor

@meshcollider meshcollider 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 8bf789b

I like that this coin selection algorithm is only 20 lines 😄

std::set<CInputCoin> out_set;
CAmount value_ret = 0;

std::vector<size_t> indexes;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: indices > indexes :)

@laanwj
Copy link
Member

laanwj commented Sep 27, 2021

Concept and code review ACK 3633b66

@meshcollider meshcollider merged commit d5d0a5c into bitcoin:master Sep 28, 2021
@maflcko
Copy link
Member

maflcko commented Sep 28, 2021

Does this cause the intermittent wallet_basic issues?

 test  2021-09-28T12:06:48.167000Z TestFramework (ERROR): Assertion failed 
                                   Traceback (most recent call last):
                                     File "/tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/test/functional/test_framework/test_framework.py", line 131, in main
                                       self.run_test()
                                     File "/tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/test/functional/wallet_basic.py", line 500, in run_test
                                       assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb))
                                     File "/tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/test/functional/test_framework/util.py", line 41, in assert_fee_amount
                                       raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee)))
                                   AssertionError: Fee of 0.00000255 BTC too low! (Should be 0.00000256 BTC)

https://cirrus-ci.com/task/6110688373374976?logs=ci#L3915

@achow101
Copy link
Member Author

Does this cause the intermittent wallet_basic issues?

Potentially. The main thing is that this change makes coin selection non-deterministic which means that tests which rely on assuming certain UTXOs are being selected are no longer correct. I thought I got all of the tests that did that, but it is possible that a few were missed.

@S3RK
Copy link
Contributor

S3RK commented Sep 30, 2021

Post merge ACK 3633b66

Reviewed code and independently reproduced simulation results. Confirmed that adding SRD increases amount of changeless solutions.

@maflcko
Copy link
Member

maflcko commented Mar 22, 2022

I think this picked up #13307 (comment), so I removed the "up for grabs" label there.

@bitcoin bitcoin locked and limited conversation to collaborators Mar 22, 2023
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.