Fix CreateDenominated failure for addresses with huge amount of inputs (#2486)

* Allow specifing max number of outpoints to be returned per address in SelectCoinsGroupedByAddresses

Fixes CreateDenominated failure for addresses with huge amount of inputs

* Apply suggested fixes
This commit is contained in:
UdjinM6 2018-11-25 16:27:31 +03:00 committed by GitHub
parent 0123517b48
commit 9d4df466b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 20 additions and 11 deletions

View File

@ -1444,8 +1444,12 @@ bool CPrivateSendClientSession::CreateDenominated(CConnman& connman)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
// NOTE: We do not allow txes larger than 100kB, so we have to limit number of inputs here.
// We still want to consume a lot of inputs to avoid creating only smaller denoms though.
// Knowing that each CTxIn is at least 148b big, 400 inputs should take 400 x ~148b = ~60kB.
// This still leaves more than enough room for another data of typical CreateDenominated tx.
std::vector<CompactTallyItem> vecTally; std::vector<CompactTallyItem> vecTally;
if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally)) { if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally, true, true, true, 400)) {
LogPrint("privatesend", "CPrivateSendClientSession::CreateDenominated -- SelectCoinsGroupedByAddresses can't find any inputs!\n"); LogPrint("privatesend", "CPrivateSendClientSession::CreateDenominated -- SelectCoinsGroupedByAddresses can't find any inputs!\n");
return false; return false;
} }

View File

@ -3069,14 +3069,14 @@ struct CompareByAmount
} }
}; };
bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTallyRet, bool fSkipDenominated, bool fAnonymizable, bool fSkipUnconfirmed) const bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTallyRet, bool fSkipDenominated, bool fAnonymizable, bool fSkipUnconfirmed, int nMaxOupointsPerAddress) const
{ {
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
isminefilter filter = ISMINE_SPENDABLE; isminefilter filter = ISMINE_SPENDABLE;
// try to use cache for already confirmed anonymizable inputs // try to use cache for already confirmed anonymizable inputs, no cache should be used when the limit is specified
if(fAnonymizable && fSkipUnconfirmed) { if(nMaxOupointsPerAddress != -1 && fAnonymizable && fSkipUnconfirmed) {
if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) { if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) {
vecTallyRet = vecAnonymizableTallyCachedNonDenom; vecTallyRet = vecAnonymizableTallyCachedNonDenom;
LogPrint("selectcoins", "SelectCoinsGroupedByAddresses - using cache for non-denom inputs %d\n", vecTallyRet.size()); LogPrint("selectcoins", "SelectCoinsGroupedByAddresses - using cache for non-denom inputs %d\n", vecTallyRet.size());
@ -3114,6 +3114,9 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
isminefilter mine = ::IsMine(*this, txdest); isminefilter mine = ::IsMine(*this, txdest);
if(!(mine & filter)) continue; if(!(mine & filter)) continue;
auto itTallyItem = mapTally.find(txdest);
if (nMaxOupointsPerAddress != -1 && itTallyItem != mapTally.end() && itTallyItem->second.vecOutPoints.size() >= nMaxOupointsPerAddress) continue;
if(IsSpent(outpoint.hash, i) || IsLockedCoin(outpoint.hash, i)) continue; if(IsSpent(outpoint.hash, i) || IsLockedCoin(outpoint.hash, i)) continue;
if(fSkipDenominated && CPrivateSend::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue; if(fSkipDenominated && CPrivateSend::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue;
@ -3129,10 +3132,12 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
if(GetCappedOutpointPrivateSendRounds(COutPoint(outpoint.hash, i)) >= privateSendClient.nPrivateSendRounds) continue; if(GetCappedOutpointPrivateSendRounds(COutPoint(outpoint.hash, i)) >= privateSendClient.nPrivateSendRounds) continue;
} }
CompactTallyItem& item = mapTally[txdest]; if (itTallyItem == mapTally.end()) {
item.txdest = txdest; itTallyItem = mapTally.emplace(txdest, CompactTallyItem()).first;
item.nAmount += wtx.tx->vout[i].nValue; itTallyItem->second.txdest = txdest;
item.vecOutPoints.emplace_back(outpoint.hash, i); }
itTallyItem->second.nAmount += wtx.tx->vout[i].nValue;
itTallyItem->second.vecOutPoints.emplace_back(outpoint.hash, i);
} }
} }
@ -3146,8 +3151,8 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
// order by amounts per address, from smallest to largest // order by amounts per address, from smallest to largest
std::sort(vecTallyRet.rbegin(), vecTallyRet.rend(), CompareByAmount()); std::sort(vecTallyRet.rbegin(), vecTallyRet.rend(), CompareByAmount());
// cache already confirmed anonymizable entries for later use // cache already confirmed anonymizable entries for later use, no cache should be saved when the limit is specified
if(fAnonymizable && fSkipUnconfirmed) { if(nMaxOupointsPerAddress != -1 && fAnonymizable && fSkipUnconfirmed) {
if(fSkipDenominated) { if(fSkipDenominated) {
vecAnonymizableTallyCachedNonDenom = vecTallyRet; vecAnonymizableTallyCachedNonDenom = vecTallyRet;
fAnonymizableTallyCachedNonDenom = true; fAnonymizableTallyCachedNonDenom = true;

View File

@ -810,7 +810,7 @@ public:
bool GetCollateralTxDSIn(CTxDSIn& txdsinRet, CAmount& nValueRet) const; bool GetCollateralTxDSIn(CTxDSIn& txdsinRet, CAmount& nValueRet) const;
bool SelectPrivateCoins(CAmount nValueMin, CAmount nValueMax, std::vector<CTxIn>& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const; bool SelectPrivateCoins(CAmount nValueMin, CAmount nValueMax, std::vector<CTxIn>& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const;
bool SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTallyRet, bool fSkipDenominated = true, bool fAnonymizable = true, bool fSkipUnconfirmed = true) const; bool SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTallyRet, bool fSkipDenominated = true, bool fAnonymizable = true, bool fSkipUnconfirmed = true, int nMaxOupointsPerAddress = -1) const;
/// Get 1000DASH output and keys which can be used for the Masternode /// Get 1000DASH output and keys which can be used for the Masternode
bool GetMasternodeOutpointAndKeys(COutPoint& outpointRet, CPubKey& pubKeyRet, CKey& keyRet, const std::string& strTxHash = "", const std::string& strOutputIndex = ""); bool GetMasternodeOutpointAndKeys(COutPoint& outpointRet, CPubKey& pubKeyRet, CKey& keyRet, const std::string& strTxHash = "", const std::string& strOutputIndex = "");