Skip to content

Commit

Permalink
Fix CreateDenominated failure for addresses with huge amount of inputs (
Browse files Browse the repository at this point in the history
#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
  • Loading branch information
UdjinM6 authored Nov 25, 2018
1 parent 0123517 commit 9d4df46
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 11 deletions.
6 changes: 5 additions & 1 deletion src/privatesend-client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1444,8 +1444,12 @@ bool CPrivateSendClientSession::CreateDenominated(CConnman& connman)

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;
if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally)) {
if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally, true, true, true, 400)) {
LogPrint("privatesend", "CPrivateSendClientSession::CreateDenominated -- SelectCoinsGroupedByAddresses can't find any inputs!\n");
return false;
}
Expand Down
23 changes: 14 additions & 9 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

isminefilter filter = ISMINE_SPENDABLE;

// try to use cache for already confirmed anonymizable inputs
if(fAnonymizable && fSkipUnconfirmed) {
// try to use cache for already confirmed anonymizable inputs, no cache should be used when the limit is specified
if(nMaxOupointsPerAddress != -1 && fAnonymizable && fSkipUnconfirmed) {
if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) {
vecTallyRet = vecAnonymizableTallyCachedNonDenom;
LogPrint("selectcoins", "SelectCoinsGroupedByAddresses - using cache for non-denom inputs %d\n", vecTallyRet.size());
Expand Down Expand Up @@ -3114,6 +3114,9 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
isminefilter mine = ::IsMine(*this, txdest);
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(fSkipDenominated && CPrivateSend::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue;
Expand All @@ -3129,10 +3132,12 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
if(GetCappedOutpointPrivateSendRounds(COutPoint(outpoint.hash, i)) >= privateSendClient.nPrivateSendRounds) continue;
}

CompactTallyItem& item = mapTally[txdest];
item.txdest = txdest;
item.nAmount += wtx.tx->vout[i].nValue;
item.vecOutPoints.emplace_back(outpoint.hash, i);
if (itTallyItem == mapTally.end()) {
itTallyItem = mapTally.emplace(txdest, CompactTallyItem()).first;
itTallyItem->second.txdest = txdest;
}
itTallyItem->second.nAmount += wtx.tx->vout[i].nValue;
itTallyItem->second.vecOutPoints.emplace_back(outpoint.hash, i);
}
}

Expand All @@ -3146,8 +3151,8 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
// order by amounts per address, from smallest to largest
std::sort(vecTallyRet.rbegin(), vecTallyRet.rend(), CompareByAmount());

// cache already confirmed anonymizable entries for later use
if(fAnonymizable && fSkipUnconfirmed) {
// cache already confirmed anonymizable entries for later use, no cache should be saved when the limit is specified
if(nMaxOupointsPerAddress != -1 && fAnonymizable && fSkipUnconfirmed) {
if(fSkipDenominated) {
vecAnonymizableTallyCachedNonDenom = vecTallyRet;
fAnonymizableTallyCachedNonDenom = true;
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
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 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
bool GetMasternodeOutpointAndKeys(COutPoint& outpointRet, CPubKey& pubKeyRet, CKey& keyRet, const std::string& strTxHash = "", const std::string& strOutputIndex = "");
Expand Down

0 comments on commit 9d4df46

Please sign in to comment.