diff options
| author | Gavin Andresen <[email protected]> | 2012-06-13 21:36:35 -0400 |
|---|---|---|
| committer | Gavin Andresen <[email protected]> | 2012-06-13 21:36:35 -0400 |
| commit | d518e444f83c7b88422f9bb8bbc89cefb2272b88 (patch) | |
| tree | 9e3f6fb194d104eda0423acafb5baa538605cdec /src/wallet.cpp | |
| parent | Merge pull request #1448 from Diapolo/2012-06-13_translation_updates (diff) | |
| parent | Fix coin selection to only include change when it's necessary. (diff) | |
| download | discoin-d518e444f83c7b88422f9bb8bbc89cefb2272b88.tar.xz discoin-d518e444f83c7b88422f9bb8bbc89cefb2272b88.zip | |
Merge branch 'refactor_coinselect' of https://github.com/luke-jr/bitcoin
Diffstat (limited to 'src/wallet.cpp')
| -rw-r--r-- | src/wallet.cpp | 192 |
1 files changed, 110 insertions, 82 deletions
diff --git a/src/wallet.cpp b/src/wallet.cpp index 4e3b559f6..127d58080 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -17,6 +17,15 @@ using namespace std; // mapWallet // +struct CompareValueOnly +{ + bool operator()(const pair<int64, pair<const CWalletTx*, unsigned int> >& t1, + const pair<int64, pair<const CWalletTx*, unsigned int> >& t2) const + { + return t1.first < t2.first; + } +}; + CPubKey CWallet::GenerateNewKey() { bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets @@ -889,98 +898,39 @@ int64 CWallet::GetImmatureBalance() const return nTotal; } -bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const +// populate vCoins with vector of spendable COutputs +void CWallet::AvailableCoins(vector<COutput>& vCoins) const { - setCoinsRet.clear(); - nValueRet = 0; - - // List of values less than target - pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger; - coinLowestLarger.first = std::numeric_limits<int64>::max(); - coinLowestLarger.second.first = NULL; - vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue; - int64 nTotalLower = 0; + vCoins.clear(); { - LOCK(cs_wallet); - vector<const CWalletTx*> vCoins; - vCoins.reserve(mapWallet.size()); - for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - vCoins.push_back(&(*it).second); - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - - BOOST_FOREACH(const CWalletTx* pcoin, vCoins) - { + LOCK(cs_wallet); + for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) - continue; - for (unsigned int i = 0; i < pcoin->vout.size(); i++) - { - if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i])) - continue; - - int64 n = pcoin->vout[i].nValue; - - if (n <= 0) - continue; - - pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i)); - - if (n == nTargetValue) - { - setCoinsRet.insert(coin.second); - nValueRet += coin.first; - return true; - } - else if (n < nTargetValue + CENT) - { - vValue.push_back(coin); - nTotalLower += n; - } - else if (n < coinLowestLarger.first) - { - coinLowestLarger = coin; - } - } - } - } - - if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) - { - for (unsigned int i = 0; i < vValue.size(); ++i) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; + if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0) + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); } - return true; - } - - if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) - { - if (coinLowestLarger.second.first == NULL) - return false; - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; - return true; } +} - if (nTotalLower >= nTargetValue + CENT) - nTargetValue += CENT; - - // Solve subset sum by stochastic approximation - sort(vValue.rbegin(), vValue.rend()); +static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsigned int> > >vValue, int64 nTotalLower, int64 nTargetValue, + vector<char>& vfBest, int64& nBest, int iterations = 1000) +{ vector<char> vfIncluded; - vector<char> vfBest(vValue.size(), true); - int64 nBest = nTotalLower; - for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) + vfBest.assign(vValue.size(), true); + nBest = nTotalLower; + + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); int64 nTotal = 0; @@ -1008,9 +958,84 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe } } } +} + +bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins, + set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits<int64>::max(); + coinLowestLarger.second.first = NULL; + vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue; + int64 nTotalLower = 0; + + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(COutput output, vCoins) + { + const CWalletTx *pcoin = output.tx; + + if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + continue; + + int i = output.i; + int64 n = pcoin->vout[i].nValue; + + pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i)); + + if (n == nTargetValue) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } + else if (n < nTargetValue + CENT) + { + vValue.push_back(coin); + nTotalLower += n; + } + else if (n < coinLowestLarger.first) + { + coinLowestLarger = coin; + } + } + + if (nTotalLower == nTargetValue) + { + for (unsigned int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } - // If the next larger is still closer, return it - if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) + if (nTotalLower < nTargetValue) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); + vector<char> vfBest; + int64 nBest; + + ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) + ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000); + + // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, + // or the next bigger coin is closer), return the bigger coin + if (coinLowestLarger.second.first && + ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest)) { setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; @@ -1036,9 +1061,12 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe bool CWallet::SelectCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const { - return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); + vector<COutput> vCoins; + AvailableCoins(vCoins); + + return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)); } |