peercoin / coinlib

The most feature-complete *coin library in the entire Dart/Flutter ecosystem.
BSD 3-Clause "New" or "Revised" License
6 stars 11 forks source link

UTXO/Coin Selection #3

Open MatthewLM opened 1 year ago

MatthewLM commented 1 year ago

One or more algorithms can be produced to select UTXOs when given a list of UTXO values and ages to help with transaction construction. A class could be created called CoinSelector that can construct a transaction from a UTXO set and desired outputs.

What should this algorithm optimize for? Should it contain options for the optimisation process, or should there be multiple sub-classes with different optimization algorithms?

Tasks

Private Random Improve

A target value shall be set for the total recipient value times 2, such that the change is near to the recipient value. It shall be determined if the total input value minus fee should be above or below the target on a 50/50 basis.

UTXOs will be randomised with inputs for identical programs being grouped together. UTXOs will be added from this list until the target plus fee is exceeded.

Once the input value minus fee exceeds the target, two things may happen. If the amount should be under the target, remove the last output and add to a discard list. If the amount is above target*0.75 then return, else go back to adding further outputs.

If the amount should be above the target, return if the amount is under target*1.5, else remove the last output to the discard list and continue to add further outputs.

If there are no more outputs left, it shall return provided that the required amount is met. Otherwise it shall add outputs from the discard list until the required amount is met.

peerchemist commented 1 year ago

The primary use case of the coinlib is the mobile and web light wallet made in flutter, which is not capable of minting. Given this fact, it is useless to optimize UTXO selection for output age. Probably the best approach is the simplest one, with minor optimization like avoiding dust or avoiding change as secondary goals.

MatthewLM commented 1 year ago

Avoiding change can be done through the BnB algorithm ported from the C++ client.

If we use BnB but no solution is found, then we can use a fallback algorithm. To avoid dust we can adapt the idea of the "Random-Improve" algorithm: https://iohk.io/en/blog/posts/2018/07/03/self-organisation-in-coin-selection/

For this fallback, I suggest sorting outputs by address so that utxos of a given address are tried together before moving onto the next one. The sorting can be otherwise randomised. If the transaction size is too large, a largest-first sorting can be tried instead.

For additional privacy I think that after the payment value is met, we could randomly target a range of 1 + 1/sqrt(2) or 1 + sqrt(2) of the payment value so that the change is randomly either higher or lower than the payment value. This makes it harder to determine which output is the change. With a determined range selected, we can add outputs until the lower bound is met. If the upper bound is then exceeded, the smallest utxos can be removed until it falls below the upper bound. If we then fall below the lower bound again, we can continue to add further utxos and repeat the process until we run out of utxos or fall within the desired range.

This process of adding and removing utxos until we fall within range could be used to target the change-free range instead of BnB and would allow a single algorithm to cover the different approaches. It wouldn't try as many options as BnB however and could miss solutions.

peerchemist commented 1 year ago

For the start, it is probably the best to port the solution from the C++ client.

MatthewLM commented 1 year ago

The BnB can be ported. I wouldn't bother with Knapsack. The Random-Improve algorithm should be a reasonable fallback and I think the changes I propose can enhance it further for privacy.

peerchemist commented 1 year ago

Okay, make it so.

MatthewLM commented 1 year ago

OK, I will port BnB and then an adapted privacy-enhanced version of Random-Improve as a fallback. Ultimate fallback, in case of too many inputs, will be to use largest-first.