diff --git a/src/darksend.cpp b/src/darksend.cpp index ed89f20bb..2033e4893 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -14,6 +14,7 @@ #include "script/sign.h" #include "txmempool.h" #include "util.h" +#include "utilmoneystr.h" #include @@ -1686,7 +1687,7 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin if nMinRounds >= 0 it means only denominated inputs are going in and coming out */ - bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, 0.1*COIN, PRIVATESEND_POOL_MAX, vecTxIn, vCoins, nValueIn, nMinRounds, nMaxRounds); + bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecPrivateSendDenominations.back(), PRIVATESEND_POOL_MAX, vecTxIn, vCoins, nValueIn, nMinRounds, nMaxRounds); if (nMinRounds >= 0 && !fSelected) { strErrorRet = "Can't select current denominated inputs"; return false; @@ -1703,65 +1704,57 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin CAmount nValueLeft = nValueIn; - /* - TODO: Front load with needed denominations (e.g. .1, 1 ) - */ - - // Make outputs by looping through denominations: try to add every needed denomination, repeat up to 5-10 times. - // This way we can be pretty sure that it should have at least one of each needed denomination. + // Try to add every needed denomination, repeat up to 5-9 times. // NOTE: No need to randomize order of inputs because they were // initially shuffled in CWallet::SelectCoinsByDenominations already. int nStep = 0; int nStepsMax = 5 + GetRandInt(5); - while(nStep < nStepsMax) { + std::vector vecBits; + if (!GetDenominationsBits(nSessionDenom, vecBits)) { + strErrorRet = "Incorrect session denom"; + return false; + } - BOOST_FOREACH(CAmount nValueDenom, vecPrivateSendDenominations) { - // only use the ones that are approved - if (!((nSessionDenom & (1 << 0)) && nValueDenom == 100*COIN +100000) && - !((nSessionDenom & (1 << 1)) && nValueDenom == 10*COIN + 10000) && - !((nSessionDenom & (1 << 2)) && nValueDenom == 1*COIN + 1000) && - !((nSessionDenom & (1 << 3)) && nValueDenom == .1*COIN + 100)) - { continue; } + while (nStep < nStepsMax) { + BOOST_FOREACH(int nBit, vecBits) { + CAmount nValueDenom = vecPrivateSendDenominations[nBit]; + if (nValueLeft - nValueDenom < 0) continue; - // try to add it - if (nValueLeft - nValueDenom >= 0) { - // Note: this relies on a fact that both vectors MUST have same size - std::vector::iterator it = vecTxIn.begin(); - std::vector::iterator it2 = vCoins.begin(); - while (it2 != vCoins.end()) { - // we have matching inputs - if ((*it2).tx->vout[(*it2).i].nValue == nValueDenom) { - // add new input in resulting vector - vecTxInRet.push_back(*it); - // remove corresponting items from initial vectors - vecTxIn.erase(it); - vCoins.erase(it2); + // Note: this relies on a fact that both vectors MUST have same size + std::vector::iterator it = vecTxIn.begin(); + std::vector::iterator it2 = vCoins.begin(); + while (it2 != vCoins.end()) { + // we have matching inputs + if ((*it2).tx->vout[(*it2).i].nValue == nValueDenom) { + // add new input in resulting vector + vecTxInRet.push_back(*it); + // remove corresponting items from initial vectors + vecTxIn.erase(it); + vCoins.erase(it2); - CScript scriptChange; - CPubKey vchPubKey; - // use a unique change address - assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked - scriptChange = GetScriptForDestination(vchPubKey.GetID()); - reservekey.KeepKey(); + CScript scriptChange; + CPubKey vchPubKey; + // use a unique change address + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + reservekey.KeepKey(); - // add new output - CTxOut txout(nValueDenom, scriptChange); - vecTxOutRet.push_back(txout); + // add new output + CTxOut txout(nValueDenom, scriptChange); + vecTxOutRet.push_back(txout); - // subtract denomination amount - nValueLeft -= nValueDenom; + // subtract denomination amount + nValueLeft -= nValueDenom; - break; - } - ++it; - ++it2; + // step is complete + break; } + ++it; + ++it2; } } - - nStep++; - if(nValueLeft == 0) break; + nStep++; } { @@ -2071,38 +2064,34 @@ bool CDarksendPool::IsDenomCompatibleWithSession(int nDenom, CTransaction txColl } /* Create a nice string to show the denominations - Function returns as follows: + Function returns as follows (for 4 denominations): ( bit on if present ) bit 0 - 100 bit 1 - 10 bit 2 - 1 bit 3 - .1 + bit 4 and so on - out-of-bounds none of above - non-denom - bit 4 and so on - non-denom */ std::string CDarksendPool::GetDenominationsToString(int nDenom) { - std::string strDenom; + std::string strDenom = ""; + int nMaxDenoms = vecPrivateSendDenominations.size(); - if(nDenom & (1 << 0)) strDenom += "100"; - - if(nDenom & (1 << 1)) { - if(strDenom.size() > 0) strDenom += "+"; - strDenom += "10"; + if(nDenom >= (1 << nMaxDenoms)) { + return "out-of-bounds"; } - if(nDenom & (1 << 2)) { - if(strDenom.size() > 0) strDenom += "+"; - strDenom += "1"; + for (int i = 0; i < nMaxDenoms; ++i) { + if(nDenom & (1 << i)) { + strDenom += (strDenom.empty() ? "" : "+") + FormatMoney(vecPrivateSendDenominations[i]); + } } - if(nDenom & (1 << 3)) { - if(strDenom.size() > 0) strDenom += "+"; - strDenom += "0.1"; + if(strDenom.empty()) { + return "non-denom"; } - if(strDenom.size() == 0 && nDenom >= (1 << 4)) strDenom += "non-denom"; - return strDenom; } @@ -2117,7 +2106,7 @@ int CDarksendPool::GetDenominations(const std::vector& vecTxDSOut) } /* Return a bitshifted integer representing the denominations in this list - Function returns as follows: + Function returns as follows (for 4 denominations): ( bit on if present ) 100 - bit 0 10 - bit 1 @@ -2157,6 +2146,29 @@ int CDarksendPool::GetDenominations(const std::vector& vecTxOut, bool fS return nDenom; } +bool CDarksendPool::GetDenominationsBits(int nDenom, std::vector &vecBitsRet) +{ + // ( bit on if present, 4 denominations example ) + // bit 0 - 100DASH+1 + // bit 1 - 10DASH+1 + // bit 2 - 1DASH+1 + // bit 3 - .1DASH+1 + + int nMaxDenoms = vecPrivateSendDenominations.size(); + + if(nDenom >= (1 << nMaxDenoms)) return false; + + vecBitsRet.clear(); + + for (int i = 0; i < nMaxDenoms; ++i) { + if(nDenom & (1 << i)) { + vecBitsRet.push_back(i); + } + } + + return !vecBitsRet.empty(); +} + int CDarksendPool::GetDenominationsByAmounts(const std::vector& vecAmount) { CScript scriptTmp = CScript(); diff --git a/src/darksend.h b/src/darksend.h index 6018a1ae7..b38f114fc 100644 --- a/src/darksend.h +++ b/src/darksend.h @@ -446,6 +446,7 @@ public: int GetDenominations(const std::vector& vecTxOut, bool fSingleRandomDenom = false); int GetDenominations(const std::vector& vecTxDSOut); std::string GetDenominationsToString(int nDenom); + bool GetDenominationsBits(int nDenom, std::vector &vecBitsRet); void SetMinBlockSpacing(int nMinBlockSpacingIn) { nMinBlockSpacing = nMinBlockSpacingIn; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6d18a397e..f05c55e0f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2331,12 +2331,12 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int return true; } -bool CWallet::SelectCoins(const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, AvailableCoinsType coin_type, bool fUseInstantSend) const +bool CWallet::SelectCoins(const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, AvailableCoinsType nCoinType, bool fUseInstantSend) const { // Note: this function should never be used for "always free" tx types like dstx vector vCoins; - AvailableCoins(vCoins, true, coinControl, false, coin_type, fUseInstantSend); + AvailableCoins(vCoins, true, coinControl, false, nCoinType, fUseInstantSend); // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) @@ -2346,11 +2346,11 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, setGetHash(),out.i); - int rounds = GetInputPrivateSendRounds(vin); + if(nCoinType == ONLY_DENOMINATED) { + CTxIn txin = CTxIn(out.tx->GetHash(),out.i); + int nRounds = GetInputPrivateSendRounds(txin); // make sure it's actually anonymized - if(rounds < nPrivateSendRounds) continue; + if(nRounds < nPrivateSendRounds) continue; } nValueRet += out.tx->vout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); @@ -2358,21 +2358,21 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set= nTargetValue); } - //if we're doing only denominated, we need to round up to the nearest .1DRK - if(coin_type == ONLY_DENOMINATED) { + //if we're doing only denominated, we need to round up to the nearest smallest denomination + if(nCoinType == ONLY_DENOMINATED) { + CAmount nSmallestDenom = vecPrivateSendDenominations.back(); // Make outputs by looping through denominations, from large to small - BOOST_FOREACH(CAmount v, vecPrivateSendDenominations) + BOOST_FOREACH(CAmount nDenom, vecPrivateSendDenominations) { BOOST_FOREACH(const COutput& out, vCoins) { - if(out.tx->vout[out.i].nValue == v //make sure it's the denom we're looking for - && nValueRet + out.tx->vout[out.i].nValue < nTargetValue + (0.1*COIN)+100 //round the amount up to .1DRK over - ){ - CTxIn vin = CTxIn(out.tx->GetHash(),out.i); - int rounds = GetInputPrivateSendRounds(vin); + //make sure it's the denom we're looking for, round the amount up to smallest denom + if(out.tx->vout[out.i].nValue == nDenom && nValueRet + nDenom < nTargetValue + nSmallestDenom) { + CTxIn txin = CTxIn(out.tx->GetHash(),out.i); + int nRounds = GetInputPrivateSendRounds(txin); // make sure it's actually anonymized - if(rounds < nPrivateSendRounds) continue; - nValueRet += out.tx->vout[out.i].nValue; + if(nRounds < nPrivateSendRounds) continue; + nValueRet += nDenom; setCoinsRet.insert(make_pair(out.tx, out.i)); } } @@ -2480,19 +2480,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) { vecTxInRet.clear(); + vCoinsRet.clear(); nValueRet = 0; - vCoinsRet.clear(); vector vCoins; AvailableCoins(vCoins, true, NULL, false, ONLY_DENOMINATED); - std::random_shuffle(vCoins.rbegin(), vCoins.rend()); - - //keep track of each denomination that we have - bool fFound100 = false; - bool fFound10 = false; - bool fFound1 = false; - bool fFoundDot1 = false; + std::random_shuffle(vCoins.rbegin(), vCoins.rend(), GetRandInt); // ( bit on if present ) // bit 0 - 100DASH+1 @@ -2500,56 +2494,45 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount // bit 2 - 1DASH+1 // bit 3 - .1DASH+1 - //Check to see if any of the denomination are off, in that case mark them as fulfilled - if(!(nDenom & (1 << 0))) fFound100 = true; - if(!(nDenom & (1 << 1))) fFound10 = true; - if(!(nDenom & (1 << 2))) fFound1 = true; - if(!(nDenom & (1 << 3))) fFoundDot1 = true; + std::vector vecBits; + if (!darkSendPool.GetDenominationsBits(nDenom, vecBits)) { + return false; + } + + int nDenomResult = 0; BOOST_FOREACH(const COutput& out, vCoins) { // masternode-like input should not be selected by AvailableCoins now anyway //if(out.tx->vout[out.i].nValue == 1000*COIN) continue; if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ - bool fAccepted = false; - CTxIn txin = CTxIn(out.tx->GetHash(),out.i); + CTxIn txin = CTxIn(out.tx->GetHash(), out.i); int nRounds = GetInputPrivateSendRounds(txin); if(nRounds >= nPrivateSendRoundsMax) continue; if(nRounds < nPrivateSendRoundsMin) continue; - if(fFound100 && fFound10 && fFound1 && fFoundDot1){ //if fulfilled - //we can return this for submission - if(nValueRet >= nValueMin){ - //random reduce the max amount we'll submit for anonymity - nValueMax -= (rand() % (nValueMax/5)); - //on average use 50% of the inputs or less - int r = (rand() % (int)vCoins.size()); - if((int)vecTxInRet.size() > r) return true; + BOOST_FOREACH(int nBit, vecBits) { + if(out.tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) { + if(nValueRet >= nValueMin) { + //randomly reduce the max amount we'll submit (for anonymity) + nValueMax -= (GetInsecureRand(nValueMax/5)); + //on average use 50% of the inputs or less + int r = GetInsecureRand((int)vCoins.size()); + if((int)vecTxInRet.size() > r) return true; + } + txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + vecTxInRet.push_back(txin); + vCoinsRet.push_back(out); + nDenomResult |= 1 << nBit; } - //Denomination criterion has been met, we can take any matching denominations - if((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((100*COIN) +100000)) {fAccepted = true;} - else if((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((10*COIN)+10000)) {fAccepted = true;} - else if((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((1*COIN) +1000)) {fAccepted = true;} - else if((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((.1*COIN)+100)) {fAccepted = true;} - } else { - //Criterion has not been satisfied, we will only take 1 of each until it is. - if((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((100*COIN) +100000)) {fAccepted = true; fFound100 = true;} - else if((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((10*COIN)+10000)) {fAccepted = true; fFound10 = true;} - else if((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((1*COIN) +1000)) {fAccepted = true; fFound1 = true;} - else if((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((.1*COIN)+100)) {fAccepted = true; fFoundDot1 = true;} } - if(!fAccepted) continue; - - txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey - nValueRet += out.tx->vout[out.i].nValue; - vecTxInRet.push_back(txin); - vCoinsRet.push_back(out); } } - return (nValueRet >= nValueMin && fFound100 && fFound10 && fFound1 && fFoundDot1); + return nValueRet >= nValueMin && nDenom == nDenomResult; } struct CompareByAmount diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 84db20967..af2122d94 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -518,7 +518,7 @@ private: * all coins from coinControl are selected; Never select unconfirmed coins * if they are not ours */ - bool SelectCoins(const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS, bool fUseInstantSend = true) const; + bool SelectCoins(const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = true) const; CWalletDB *pwalletdbEncryption;