Drop custom PS logic for guessing fees etc. from SelectCoins (#2371)

* drop most of custom PS logic from SelectCoins and inject it into regular flow (i.e. SelectCoinsMinConf)

* different priority for different denoms

* move CompareByPriority higher

* update comment

Co-Authored-By: UdjinM6 <udjinm6@gmail.com>
This commit is contained in:
UdjinM6 2018-11-04 14:55:16 +03:00 committed by GitHub
parent 02442673d2
commit 23f169c44a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 51 deletions

View File

@ -86,8 +86,10 @@ std::string COutput::ToString() const
int COutput::Priority() const int COutput::Priority() const
{ {
for (const auto& d : CPrivateSend::GetStandardDenominations()) for (const auto& d : CPrivateSend::GetStandardDenominations()) {
if(tx->tx->vout[i].nValue == d) return 10000; // large denoms have lower value
if(tx->tx->vout[i].nValue == d) return (float)COIN / d * 10000;
}
if(tx->tx->vout[i].nValue < 1*COIN) return 20000; if(tx->tx->vout[i].nValue < 1*COIN) return 20000;
//nondenom return largest first //nondenom return largest first
@ -2687,6 +2689,15 @@ static void ApproximateBestSubset(std::vector<std::pair<CAmount, std::pair<const
} }
} }
struct CompareByPriority
{
bool operator()(const COutput& t1,
const COutput& t2) const
{
return t1.Priority() > t2.Priority();
}
};
// move denoms down // move denoms down
bool less_then_denom (const COutput& out1, const COutput& out2) bool less_then_denom (const COutput& out1, const COutput& out2)
{ {
@ -2704,7 +2715,7 @@ bool less_then_denom (const COutput& out1, const COutput& out2)
} }
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector<COutput> vCoins, bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector<COutput> vCoins,
std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, bool fUseInstantSend) const std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, AvailableCoinsType nCoinType, bool fUseInstantSend) const
{ {
setCoinsRet.clear(); setCoinsRet.clear();
nValueRet = 0; nValueRet = 0;
@ -2720,11 +2731,24 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
// move denoms down on the list int tryDenomStart = 0;
std::sort(vCoins.begin(), vCoins.end(), less_then_denom); CAmount nMinChange = MIN_CHANGE;
if (nCoinType == ONLY_DENOMINATED) {
// larger denoms first
std::sort(vCoins.rbegin(), vCoins.rend(), CompareByPriority());
// we actually want denoms only, so let's skip "non-denom only" step
tryDenomStart = 1;
// no change is allowed
nMinChange = 0;
} else {
// move denoms down on the list
// try not to use denominated coins when not needed, save denoms for privatesend
std::sort(vCoins.begin(), vCoins.end(), less_then_denom);
}
// try to find nondenom first to prevent unneeded spending of mixed coins // try to find nondenom first to prevent unneeded spending of mixed coins
for (unsigned int tryDenom = 0; tryDenom < 2; tryDenom++) for (unsigned int tryDenom = tryDenomStart; tryDenom < 2; tryDenom++)
{ {
LogPrint("selectcoins", "tryDenom: %d\n", tryDenom); LogPrint("selectcoins", "tryDenom: %d\n", tryDenom);
vValue.clear(); vValue.clear();
@ -2747,6 +2771,13 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
CAmount n = pcoin->tx->vout[i].nValue; CAmount n = pcoin->tx->vout[i].nValue;
if (tryDenom == 0 && CPrivateSend::IsDenominatedAmount(n)) continue; // we don't want denom values on first run if (tryDenom == 0 && CPrivateSend::IsDenominatedAmount(n)) continue; // we don't want denom values on first run
if (nCoinType == ONLY_DENOMINATED) {
// Make sure it's actually anonymized
COutPoint outpoint = COutPoint(pcoin->GetHash(), i);
int nRounds = GetRealOutpointPrivateSendRounds(outpoint);
if (nRounds < privateSendClient.nPrivateSendRounds) continue;
}
std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > coin = std::make_pair(n, std::make_pair(pcoin, i)); std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > coin = std::make_pair(n, std::make_pair(pcoin, i));
if (n == nTargetValue) if (n == nTargetValue)
@ -2755,7 +2786,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
nValueRet += coin.first; nValueRet += coin.first;
return true; return true;
} }
else if (n < nTargetValue + MIN_CHANGE) else if (n < nTargetValue + nMinChange)
{ {
vValue.push_back(coin); vValue.push_back(coin);
nTotalLower += n; nTotalLower += n;
@ -2789,7 +2820,9 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
} }
setCoinsRet.insert(coinLowestLarger.second); setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first; nValueRet += coinLowestLarger.first;
return true; // There is no change in PS, so we know the fee beforehand,
// can see if we exceeded the max fee and thus fail quickly.
return nCoinType == ONLY_DENOMINATED ? (nValueRet - nTargetValue <= maxTxFee) : true;
} }
// nTotalLower > nTargetValue // nTotalLower > nTargetValue
@ -2804,13 +2837,13 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
CAmount nBest; CAmount nBest;
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, fUseInstantSend); ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, fUseInstantSend);
if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) if (nBest != nTargetValue && nTotalLower >= nTargetValue + nMinChange)
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest, fUseInstantSend); ApproximateBestSubset(vValue, nTotalLower, nTargetValue + nMinChange, vfBest, nBest, fUseInstantSend);
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution, // 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 // or the next bigger coin is closer), return the bigger coin
if (coinLowestLarger.second.first && if (coinLowestLarger.second.first &&
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest)) ((nBest != nTargetValue && nBest < nTargetValue + nMinChange) || coinLowestLarger.first <= nBest))
{ {
setCoinsRet.insert(coinLowestLarger.second); setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first; nValueRet += coinLowestLarger.first;
@ -2829,7 +2862,9 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
LogPrint("selectcoins", "%s - total %s\n", s, FormatMoney(nBest)); LogPrint("selectcoins", "%s - total %s\n", s, FormatMoney(nBest));
} }
return true; // There is no change in PS, so we know the fee beforehand,
// can see if we exceeded the max fee and thus fail quickly.
return nCoinType == ONLY_DENOMINATED ? (nValueRet - nTargetValue <= maxTxFee) : true;
} }
bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, AvailableCoinsType nCoinType, bool fUseInstantSend) const bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, AvailableCoinsType nCoinType, bool fUseInstantSend) const
@ -2859,28 +2894,6 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return (nValueRet >= nTargetValue); return (nValueRet >= nTargetValue);
} }
//if we're doing only denominated, we need to round up to the nearest smallest denomination
if(nCoinType == ONLY_DENOMINATED) {
std::vector<CAmount> vecPrivateSendDenominations = CPrivateSend::GetStandardDenominations();
CAmount nSmallestDenom = vecPrivateSendDenominations.back();
// Make outputs by looping through denominations, from large to small
for (const auto& nDenom : vecPrivateSendDenominations)
{
for (const auto& out : vCoins)
{
//make sure it's the denom we're looking for, round the amount up to smallest denom
if(out.tx->tx->vout[out.i].nValue == nDenom && nValueRet + nDenom < nTargetValue + nSmallestDenom) {
COutPoint outpoint = COutPoint(out.tx->GetHash(),out.i);
int nRounds = GetCappedOutpointPrivateSendRounds(outpoint);
// make sure it's actually anonymized
if(nRounds < privateSendClient.nPrivateSendRounds) continue;
nValueRet += nDenom;
setCoinsRet.insert(std::make_pair(out.tx, out.i));
}
}
}
return (nValueRet >= nTargetValue);
}
// calculate value from preset inputs and store them // calculate value from preset inputs and store them
std::set<std::pair<const CWalletTx*, uint32_t> > setPresetCoins; std::set<std::pair<const CWalletTx*, uint32_t> > setPresetCoins;
CAmount nValueFromPresetInputs = 0; CAmount nValueFromPresetInputs = 0;
@ -2897,6 +2910,12 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// Clearly invalid input, fail // Clearly invalid input, fail
if (pcoin->tx->vout.size() <= outpoint.n) if (pcoin->tx->vout.size() <= outpoint.n)
return false; return false;
if (nCoinType == ONLY_DENOMINATED) {
// Make sure to include anonymized preset inputs only,
// even if some non-anonymized inputs were manually selected via CoinControl
int nRounds = GetRealOutpointPrivateSendRounds(outpoint);
if (nRounds < privateSendClient.nPrivateSendRounds) continue;
}
nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
setPresetCoins.insert(std::make_pair(pcoin, outpoint.n)); setPresetCoins.insert(std::make_pair(pcoin, outpoint.n));
} else } else
@ -2916,13 +2935,13 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
bool res = nTargetValue <= nValueFromPresetInputs || bool res = nTargetValue <= nValueFromPresetInputs ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet, fUseInstantSend) || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet, nCoinType, fUseInstantSend) ||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet, fUseInstantSend) || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet, nCoinType, fUseInstantSend) ||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet, fUseInstantSend)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet, nCoinType, fUseInstantSend)) ||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet, fUseInstantSend)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet, nCoinType, fUseInstantSend)) ||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet, fUseInstantSend)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet, nCoinType, fUseInstantSend)) ||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet, fUseInstantSend)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet, nCoinType, fUseInstantSend)) ||
(bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits<uint64_t>::max(), vCoins, setCoinsRet, nValueRet, fUseInstantSend)); (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits<uint64_t>::max(), vCoins, setCoinsRet, nValueRet, nCoinType, fUseInstantSend));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
@ -2933,15 +2952,6 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return res; return res;
} }
struct CompareByPriority
{
bool operator()(const COutput& t1,
const COutput& t2) const
{
return t1.Priority() > t2.Priority();
}
};
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange) bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange)
{ {
std::vector<CRecipient> vecSend; std::vector<CRecipient> vecSend;

View File

@ -803,7 +803,7 @@ public:
* completion the coin set and corresponding actual target value is * completion the coin set and corresponding actual target value is
* assembled * assembled
*/ */
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, bool fUseInstantSend = false) const; bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = false) const;
// Coin selection // Coin selection
bool SelectPSInOutPairsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet); bool SelectPSInOutPairsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet);