Refactor PS code to deal with denoms in a more generic way (#1071)

This commit is contained in:
UdjinM6 2016-10-16 23:23:17 +04:00 committed by GitHub
parent 27c445a891
commit bdcc9ab47c
4 changed files with 117 additions and 121 deletions

View File

@ -14,6 +14,7 @@
#include "script/sign.h" #include "script/sign.h"
#include "txmempool.h" #include "txmempool.h"
#include "util.h" #include "util.h"
#include "utilmoneystr.h"
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -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 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) { if (nMinRounds >= 0 && !fSelected) {
strErrorRet = "Can't select current denominated inputs"; strErrorRet = "Can't select current denominated inputs";
return false; return false;
@ -1703,65 +1704,57 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin
CAmount nValueLeft = nValueIn; CAmount nValueLeft = nValueIn;
/* // Try to add every needed denomination, repeat up to 5-9 times.
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.
// NOTE: No need to randomize order of inputs because they were // NOTE: No need to randomize order of inputs because they were
// initially shuffled in CWallet::SelectCoinsByDenominations already. // initially shuffled in CWallet::SelectCoinsByDenominations already.
int nStep = 0; int nStep = 0;
int nStepsMax = 5 + GetRandInt(5); int nStepsMax = 5 + GetRandInt(5);
while(nStep < nStepsMax) { std::vector<int> vecBits;
if (!GetDenominationsBits(nSessionDenom, vecBits)) {
strErrorRet = "Incorrect session denom";
return false;
}
BOOST_FOREACH(CAmount nValueDenom, vecPrivateSendDenominations) { while (nStep < nStepsMax) {
// only use the ones that are approved BOOST_FOREACH(int nBit, vecBits) {
if (!((nSessionDenom & (1 << 0)) && nValueDenom == 100*COIN +100000) && CAmount nValueDenom = vecPrivateSendDenominations[nBit];
!((nSessionDenom & (1 << 1)) && nValueDenom == 10*COIN + 10000) && if (nValueLeft - nValueDenom < 0) continue;
!((nSessionDenom & (1 << 2)) && nValueDenom == 1*COIN + 1000) &&
!((nSessionDenom & (1 << 3)) && nValueDenom == .1*COIN + 100))
{ continue; }
// try to add it // Note: this relies on a fact that both vectors MUST have same size
if (nValueLeft - nValueDenom >= 0) { std::vector<CTxIn>::iterator it = vecTxIn.begin();
// Note: this relies on a fact that both vectors MUST have same size std::vector<COutput>::iterator it2 = vCoins.begin();
std::vector<CTxIn>::iterator it = vecTxIn.begin(); while (it2 != vCoins.end()) {
std::vector<COutput>::iterator it2 = vCoins.begin(); // we have matching inputs
while (it2 != vCoins.end()) { if ((*it2).tx->vout[(*it2).i].nValue == nValueDenom) {
// we have matching inputs // add new input in resulting vector
if ((*it2).tx->vout[(*it2).i].nValue == nValueDenom) { vecTxInRet.push_back(*it);
// add new input in resulting vector // remove corresponting items from initial vectors
vecTxInRet.push_back(*it); vecTxIn.erase(it);
// remove corresponting items from initial vectors vCoins.erase(it2);
vecTxIn.erase(it);
vCoins.erase(it2);
CScript scriptChange; CScript scriptChange;
CPubKey vchPubKey; CPubKey vchPubKey;
// use a unique change address // use a unique change address
assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked
scriptChange = GetScriptForDestination(vchPubKey.GetID()); scriptChange = GetScriptForDestination(vchPubKey.GetID());
reservekey.KeepKey(); reservekey.KeepKey();
// add new output // add new output
CTxOut txout(nValueDenom, scriptChange); CTxOut txout(nValueDenom, scriptChange);
vecTxOutRet.push_back(txout); vecTxOutRet.push_back(txout);
// subtract denomination amount // subtract denomination amount
nValueLeft -= nValueDenom; nValueLeft -= nValueDenom;
break; // step is complete
} break;
++it;
++it2;
} }
++it;
++it2;
} }
} }
nStep++;
if(nValueLeft == 0) break; if(nValueLeft == 0) break;
nStep++;
} }
{ {
@ -2071,38 +2064,34 @@ bool CDarksendPool::IsDenomCompatibleWithSession(int nDenom, CTransaction txColl
} }
/* Create a nice string to show the denominations /* Create a nice string to show the denominations
Function returns as follows: Function returns as follows (for 4 denominations):
( bit on if present ) ( bit on if present )
bit 0 - 100 bit 0 - 100
bit 1 - 10 bit 1 - 10
bit 2 - 1 bit 2 - 1
bit 3 - .1 bit 3 - .1
bit 4 and so on - out-of-bounds
none of above - non-denom none of above - non-denom
bit 4 and so on - non-denom
*/ */
std::string CDarksendPool::GetDenominationsToString(int nDenom) 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 << nMaxDenoms)) {
return "out-of-bounds";
if(nDenom & (1 << 1)) {
if(strDenom.size() > 0) strDenom += "+";
strDenom += "10";
} }
if(nDenom & (1 << 2)) { for (int i = 0; i < nMaxDenoms; ++i) {
if(strDenom.size() > 0) strDenom += "+"; if(nDenom & (1 << i)) {
strDenom += "1"; strDenom += (strDenom.empty() ? "" : "+") + FormatMoney(vecPrivateSendDenominations[i]);
}
} }
if(nDenom & (1 << 3)) { if(strDenom.empty()) {
if(strDenom.size() > 0) strDenom += "+"; return "non-denom";
strDenom += "0.1";
} }
if(strDenom.size() == 0 && nDenom >= (1 << 4)) strDenom += "non-denom";
return strDenom; return strDenom;
} }
@ -2117,7 +2106,7 @@ int CDarksendPool::GetDenominations(const std::vector<CTxDSOut>& vecTxDSOut)
} }
/* Return a bitshifted integer representing the denominations in this list /* 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 ) ( bit on if present )
100 - bit 0 100 - bit 0
10 - bit 1 10 - bit 1
@ -2157,6 +2146,29 @@ int CDarksendPool::GetDenominations(const std::vector<CTxOut>& vecTxOut, bool fS
return nDenom; return nDenom;
} }
bool CDarksendPool::GetDenominationsBits(int nDenom, std::vector<int> &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<CAmount>& vecAmount) int CDarksendPool::GetDenominationsByAmounts(const std::vector<CAmount>& vecAmount)
{ {
CScript scriptTmp = CScript(); CScript scriptTmp = CScript();

View File

@ -446,6 +446,7 @@ public:
int GetDenominations(const std::vector<CTxOut>& vecTxOut, bool fSingleRandomDenom = false); int GetDenominations(const std::vector<CTxOut>& vecTxOut, bool fSingleRandomDenom = false);
int GetDenominations(const std::vector<CTxDSOut>& vecTxDSOut); int GetDenominations(const std::vector<CTxDSOut>& vecTxDSOut);
std::string GetDenominationsToString(int nDenom); std::string GetDenominationsToString(int nDenom);
bool GetDenominationsBits(int nDenom, std::vector<int> &vecBitsRet);
void SetMinBlockSpacing(int nMinBlockSpacingIn) { nMinBlockSpacing = nMinBlockSpacingIn; } void SetMinBlockSpacing(int nMinBlockSpacingIn) { nMinBlockSpacing = nMinBlockSpacingIn; }

View File

@ -2331,12 +2331,12 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
return true; return true;
} }
bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, AvailableCoinsType coin_type, bool fUseInstantSend) const bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& 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 // Note: this function should never be used for "always free" tx types like dstx
vector<COutput> vCoins; vector<COutput> 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) // coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs)
@ -2346,11 +2346,11 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
if(!out.fSpendable) if(!out.fSpendable)
continue; continue;
if(coin_type == ONLY_DENOMINATED) { if(nCoinType == ONLY_DENOMINATED) {
CTxIn vin = CTxIn(out.tx->GetHash(),out.i); CTxIn txin = CTxIn(out.tx->GetHash(),out.i);
int rounds = GetInputPrivateSendRounds(vin); int nRounds = GetInputPrivateSendRounds(txin);
// make sure it's actually anonymized // make sure it's actually anonymized
if(rounds < nPrivateSendRounds) continue; if(nRounds < nPrivateSendRounds) continue;
} }
nValueRet += out.tx->vout[out.i].nValue; nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i)); setCoinsRet.insert(make_pair(out.tx, out.i));
@ -2358,21 +2358,21 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
return (nValueRet >= nTargetValue); return (nValueRet >= nTargetValue);
} }
//if we're doing only denominated, we need to round up to the nearest .1DRK //if we're doing only denominated, we need to round up to the nearest smallest denomination
if(coin_type == ONLY_DENOMINATED) { if(nCoinType == ONLY_DENOMINATED) {
CAmount nSmallestDenom = vecPrivateSendDenominations.back();
// Make outputs by looping through denominations, from large to small // 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) BOOST_FOREACH(const COutput& out, vCoins)
{ {
if(out.tx->vout[out.i].nValue == v //make sure it's the denom we're looking for //make sure it's the denom we're looking for, round the amount up to smallest denom
&& nValueRet + out.tx->vout[out.i].nValue < nTargetValue + (0.1*COIN)+100 //round the amount up to .1DRK over if(out.tx->vout[out.i].nValue == nDenom && nValueRet + nDenom < nTargetValue + nSmallestDenom) {
){ CTxIn txin = CTxIn(out.tx->GetHash(),out.i);
CTxIn vin = CTxIn(out.tx->GetHash(),out.i); int nRounds = GetInputPrivateSendRounds(txin);
int rounds = GetInputPrivateSendRounds(vin);
// make sure it's actually anonymized // make sure it's actually anonymized
if(rounds < nPrivateSendRounds) continue; if(nRounds < nPrivateSendRounds) continue;
nValueRet += out.tx->vout[out.i].nValue; nValueRet += nDenom;
setCoinsRet.insert(make_pair(out.tx, out.i)); 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<CTxIn>& vecTxInRet, std::vector<COutput>& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector<CTxIn>& vecTxInRet, std::vector<COutput>& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax)
{ {
vecTxInRet.clear(); vecTxInRet.clear();
vCoinsRet.clear();
nValueRet = 0; nValueRet = 0;
vCoinsRet.clear();
vector<COutput> vCoins; vector<COutput> vCoins;
AvailableCoins(vCoins, true, NULL, false, ONLY_DENOMINATED); AvailableCoins(vCoins, true, NULL, false, ONLY_DENOMINATED);
std::random_shuffle(vCoins.rbegin(), vCoins.rend()); std::random_shuffle(vCoins.rbegin(), vCoins.rend(), GetRandInt);
//keep track of each denomination that we have
bool fFound100 = false;
bool fFound10 = false;
bool fFound1 = false;
bool fFoundDot1 = false;
// ( bit on if present ) // ( bit on if present )
// bit 0 - 100DASH+1 // bit 0 - 100DASH+1
@ -2500,56 +2494,45 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount
// bit 2 - 1DASH+1 // bit 2 - 1DASH+1
// bit 3 - .1DASH+1 // bit 3 - .1DASH+1
//Check to see if any of the denomination are off, in that case mark them as fulfilled std::vector<int> vecBits;
if(!(nDenom & (1 << 0))) fFound100 = true; if (!darkSendPool.GetDenominationsBits(nDenom, vecBits)) {
if(!(nDenom & (1 << 1))) fFound10 = true; return false;
if(!(nDenom & (1 << 2))) fFound1 = true; }
if(!(nDenom & (1 << 3))) fFoundDot1 = true;
int nDenomResult = 0;
BOOST_FOREACH(const COutput& out, vCoins) BOOST_FOREACH(const COutput& out, vCoins)
{ {
// masternode-like input should not be selected by AvailableCoins now anyway // masternode-like input should not be selected by AvailableCoins now anyway
//if(out.tx->vout[out.i].nValue == 1000*COIN) continue; //if(out.tx->vout[out.i].nValue == 1000*COIN) continue;
if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ 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); int nRounds = GetInputPrivateSendRounds(txin);
if(nRounds >= nPrivateSendRoundsMax) continue; if(nRounds >= nPrivateSendRoundsMax) continue;
if(nRounds < nPrivateSendRoundsMin) continue; if(nRounds < nPrivateSendRoundsMin) continue;
if(fFound100 && fFound10 && fFound1 && fFoundDot1){ //if fulfilled BOOST_FOREACH(int nBit, vecBits) {
//we can return this for submission if(out.tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) {
if(nValueRet >= nValueMin){ if(nValueRet >= nValueMin) {
//random reduce the max amount we'll submit for anonymity //randomly reduce the max amount we'll submit (for anonymity)
nValueMax -= (rand() % (nValueMax/5)); nValueMax -= (GetInsecureRand(nValueMax/5));
//on average use 50% of the inputs or less //on average use 50% of the inputs or less
int r = (rand() % (int)vCoins.size()); int r = GetInsecureRand((int)vCoins.size());
if((int)vecTxInRet.size() > r) return true; 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 struct CompareByAmount

View File

@ -518,7 +518,7 @@ private:
* all coins from coinControl are selected; Never select unconfirmed coins * all coins from coinControl are selected; Never select unconfirmed coins
* if they are not ours * if they are not ours
*/ */
bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS, bool fUseInstantSend = true) const; bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = true) const;
CWalletDB *pwalletdbEncryption; CWalletDB *pwalletdbEncryption;