Refactor PS code to deal with denoms in a more generic way (#1071)
This commit is contained in:
parent
27c445a891
commit
bdcc9ab47c
@ -14,6 +14,7 @@
|
||||
#include "script/sign.h"
|
||||
#include "txmempool.h"
|
||||
#include "util.h"
|
||||
#include "utilmoneystr.h"
|
||||
|
||||
#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
|
||||
*/
|
||||
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,28 +1704,22 @@ 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<int> 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<CTxIn>::iterator it = vecTxIn.begin();
|
||||
std::vector<COutput>::iterator it2 = vCoins.begin();
|
||||
@ -1751,17 +1746,15 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin
|
||||
// subtract denomination amount
|
||||
nValueLeft -= nValueDenom;
|
||||
|
||||
// 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<CTxDSOut>& 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<CTxOut>& vecTxOut, bool fS
|
||||
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)
|
||||
{
|
||||
CScript scriptTmp = CScript();
|
||||
|
@ -446,6 +446,7 @@ public:
|
||||
int GetDenominations(const std::vector<CTxOut>& vecTxOut, bool fSingleRandomDenom = false);
|
||||
int GetDenominations(const std::vector<CTxDSOut>& vecTxDSOut);
|
||||
std::string GetDenominationsToString(int nDenom);
|
||||
bool GetDenominationsBits(int nDenom, std::vector<int> &vecBitsRet);
|
||||
|
||||
void SetMinBlockSpacing(int nMinBlockSpacingIn) { nMinBlockSpacing = nMinBlockSpacingIn; }
|
||||
|
||||
|
@ -2331,12 +2331,12 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
|
||||
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
|
||||
|
||||
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)
|
||||
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs)
|
||||
@ -2346,11 +2346,11 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
||||
if(!out.fSpendable)
|
||||
continue;
|
||||
|
||||
if(coin_type == ONLY_DENOMINATED) {
|
||||
CTxIn vin = CTxIn(out.tx->GetHash(),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<pair<const CWalletTx*
|
||||
return (nValueRet >= 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<CTxIn>& vecTxInRet, std::vector<COutput>& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax)
|
||||
{
|
||||
vecTxInRet.clear();
|
||||
vCoinsRet.clear();
|
||||
nValueRet = 0;
|
||||
|
||||
vCoinsRet.clear();
|
||||
vector<COutput> 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<int> 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));
|
||||
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 = (rand() % (int)vCoins.size());
|
||||
int r = GetInsecureRand((int)vCoins.size());
|
||||
if((int)vecTxInRet.size() > r) return true;
|
||||
}
|
||||
//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);
|
||||
nDenomResult |= 1 << nBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (nValueRet >= nValueMin && fFound100 && fFound10 && fFound1 && fFoundDot1);
|
||||
return nValueRet >= nValueMin && nDenom == nDenomResult;
|
||||
}
|
||||
|
||||
struct CompareByAmount
|
||||
|
@ -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<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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user