Skip to content

Conversation

@murchandamus
Copy link
Contributor

vValue is sorted into ascending order before ApproximateBestSubset(...) is called.

Currently, after a new best set is found, the final element that was added gets removed from the set, and the inner loop continues to search through the remainder of vValue for a better set. It is however impossible for a better set to be achieved with a later element in vValue: As vValue is sorted in ascending order, any set formed with a later element will be of same size or bigger.

Therefore, the lines 1628 & 1629 are obsolete, and the inner loop should also stop on fReachedTarget.

@laanwj laanwj added the Wallet label Dec 8, 2015
@laanwj laanwj changed the title Removed obsolete lines of code, broke continuation of loop. ApproximateBestSubset: Removed obsolete lines of code, broke continuation of loop Dec 8, 2015
@maflcko
Copy link
Member

maflcko commented Dec 9, 2015

vValue is not sorted in ascending order, the whole algorithm wouldn't make sense if it was...

@murchandamus
Copy link
Contributor Author

@MarcoFalke: Sorry, apparently the links always refer to the current version and shift around because of that, however in CWallet::SelectCoinsMinConf(…) there are the following lines:

// Solve subset sum by stochastic approximation
sort(vValue.rbegin(), vValue.rend(), CompareValueOnly());
vector<char> vfBest;
CAmount nBest;

ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest);

As you can see vValue appears to get sorted into ascending order right before ApproximateBestSubset(…) gets called. I do think that it used to be different when I first started my other PR (September last year) though, because right now it does increase the probability of small inputs to get selected.

@laanwj
Copy link
Member

laanwj commented Dec 10, 2015

vValue is not sorted in ascending order, the whole algorithm wouldn't make sense if it was...

It actually is.
(or is CompareValueOnly doing something else than I assumed it does? that'd be confusing...)
Nope:

struct CompareValueOnly
{
    bool operator()(const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t1,
                    const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t2) const
    {
        return t1.first < t2.first;
    }
};

If you say this sorts it in descending order, I don't see where that happens.

@maflcko
Copy link
Member

maflcko commented Dec 10, 2015

If you say this sorts it in descending order, I don't see where that happens.

Right in the line where std::sort() is called:

sort(vValue.rbegin(), vValue.rend(), CompareValueOnly());

r means reverse and the reverse of ascending is descending?

@maflcko
Copy link
Member

maflcko commented Dec 10, 2015

links always refer to the current version and shift around because of that

@xekyo If you want to link on GitHub, don't forget to press the y-Key before copy pasting the link

@morcos
Copy link
Contributor

morcos commented Dec 10, 2015

Yikes, that's easy to overlook, perhaps a comment.

@maflcko
Copy link
Member

maflcko commented Dec 10, 2015

And a unit test because this PR should not pass travis in the first place.

@maflcko
Copy link
Member

maflcko commented Dec 12, 2015

@xekyo Can you take a look at this and add a comment above the "sort()-line"?

@laanwj
Copy link
Member

laanwj commented Dec 14, 2015

Yikes, that's easy to overlook, perhaps a comment.

Absolutely. I also had no idea that you could pass reverse iterators to std::sort and it would just work.

@murchandamus
Copy link
Contributor Author

@MarcoFalke: TIL. I was unaware of that behaviour of sort(…) with reverse iterators. From what I’m reading using reverse iterators isn’t very efficient, though.

Instead of using reverse iterators, one could replace the line

 sort(vValue.rbegin(), vValue.rend(), CompareValueOnly());

with these two

sort(vValue.begin(), vValue.end(), CompareValueOnly());
reverse(vValue.begin(), vValue.end());

It would become much more readable, wouldn’t need a comment to clarify, and perhaps perform even faster.

@maflcko
Copy link
Member

maflcko commented Jan 3, 2016

Sounds ok to me. Are you planning to write the unit test as well?

@murchandamus
Copy link
Contributor Author

@MarcoFalke: I could do that, yet I’m unsure what one would want to test. Since the sorting operation is in the middle of CWallet::SelectCoinsMinConf(…), it’s would require quite a bit of refactoring to isolate as vValue is only a transient variable in that function.

@maflcko
Copy link
Member

maflcko commented Jan 3, 2016

@xekyo I mean a "high level" test like #7193, such that your initial commit will fail travis. (Would you mind to post the inital diff or commit hash for reference and testing, I forgot to save it.)

@murchandamus
Copy link
Contributor Author

@MarcoFalke: Unfortunately, I also overwrote my commit when I rebased my branch (perhaps prematurely) and pushed it to my fork. Would it make sense to redo them and push them onto another branch?

I’ve added a test.

@maflcko
Copy link
Member

maflcko commented Jan 3, 2016

Provided by @xekyo on IRC: $ git reflog show Fix-#7182@{one.day.ago}

b9ecfc7

diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d23d54e..3d1f58c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1605,7 +1605,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
         bool fReachedTarget = false;
         for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
         {
-            for (unsigned int i = 0; i < vValue.size(); i++)
+            for (unsigned int i = 0; i < vValue.size() && !fReachedTarget; i++)
             {
                 //The solver here uses a randomized algorithm,
                 //the randomness serves no real security purpose but is just
@@ -1625,8 +1625,6 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
                             nBest = nTotal;
                             vfBest = vfIncluded;
                         }
-                        nTotal -= vValue[i].first;
-                        vfIncluded[i] = false;
                     }
                 }
             }

@murchandamus murchandamus changed the title ApproximateBestSubset: Removed obsolete lines of code, broke continuation of loop Improved readability of ApproximateBestSubset. Jan 3, 2016
@murchandamus murchandamus changed the title Improved readability of ApproximateBestSubset. Improved readability of ApproximateBestSubset, added test to check for suboptimal result of selection. Jan 3, 2016
Copy link
Member

Choose a reason for hiding this comment

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

This will select quite a bunch of the 3ct coins, I don't think you want this. Just reduce the number to 1 like #7293

Copy link
Member

Choose a reason for hiding this comment

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

@xekyo You can either get the test case from #7293 and include it here or remove the test case in this pull.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I’ve ACKed #7293 and will remove the test case here.

@murchandamus murchandamus changed the title Improved readability of ApproximateBestSubset, added test to check for suboptimal result of selection. Improved readability of ApproximateBestSubset Jan 5, 2016
@maflcko
Copy link
Member

maflcko commented Jan 5, 2016

utACK 047cf77e324817594ce58e0ff53e5d817d1a9bc1.

Nit: you could use std::sort and std:reverse to prevent follow up commits.

@murchandamus
Copy link
Contributor Author

Edited to include the nit. :)

@maflcko
Copy link
Member

maflcko commented Jan 5, 2016

trivial re-ACK 96efcad

@jonasschnelli
Copy link
Contributor

utACK 96efcad

@laanwj laanwj merged commit 96efcad into bitcoin:master Jan 20, 2016
laanwj added a commit that referenced this pull request Jan 20, 2016
96efcad Improved readability of sorting for coin selection. (Murch)
@laanwj
Copy link
Member

laanwj commented Jan 20, 2016

utACK 96efcad

@luke-jr
Copy link
Member

luke-jr commented Feb 12, 2016

Is there some reason not to just invert the CompareValueOnly function?

@laanwj
Copy link
Member

laanwj commented Feb 12, 2016

And call it ReverseCompareValueOnly, yes that would have been the other option. But let's leave it as it is now, there's no need to shedpaint this.

codablock pushed a commit to codablock/dash that referenced this pull request Dec 9, 2017
96efcad Improved readability of sorting for coin selection. (Murch)
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
@murchandamus murchandamus deleted the Fix-#7182 branch October 6, 2023 19:03
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.

6 participants