From f1a26607c2fe4b733fdbb2c0ded78f1557ef4be9 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 21 Aug 2015 18:58:56 +0300 Subject: [PATCH] fix - ds can leak info by number of inputs --- src/darksend.cpp | 111 +++++++++++++++++++++++++++++++++++---------- src/darksend.h | 22 +++++---- src/version.h | 4 +- src/wallet.cpp | 116 ++++++++++++++++++++++------------------------- src/wallet.h | 2 +- 5 files changed, 156 insertions(+), 99 deletions(-) diff --git a/src/darksend.cpp b/src/darksend.cpp index afacc7f276e1d..4d7bd994d372f 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -67,8 +67,22 @@ void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand } int nDenom; + int nNumberOfInputs; CTransaction txCollateral; - vRecv >> nDenom >> txCollateral; + vRecv >> nDenom >> nNumberOfInputs >> txCollateral; + + //non-denom's are incompatible + if((nDenom & (1 << 4))) { + errorID = ERR_INVALID_DENOM; + pfrom->PushMessage("dssu", sessionID, GetState(), GetEntriesCount(), MASTERNODE_REJECTED, errorID); + return; + } + + if(nNumberOfInputs < DARKSEND_MIN_INPUTS || nNumberOfInputs > DARKSEND_MAX_INPUTS) { + errorID = ERR_INVALID_INPUTS_NUMBER; + pfrom->PushMessage("dssu", sessionID, GetState(), GetEntriesCount(), MASTERNODE_REJECTED, errorID); + return; + } CMasternode* pmn = mnodeman.Find(activeMasternode.vin); if(pmn == NULL) @@ -88,13 +102,13 @@ void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand } } - if(!IsCompatibleWithSession(nDenom, txCollateral, errorID)) + if(!IsCompatibleWithSession(nDenom, nNumberOfInputs, txCollateral, errorID)) { - LogPrintf("dsa -- not compatible with existing transactions! \n"); + LogPrintf("dsa -- not compatible with existing transactions!\n"); pfrom->PushMessage("dssu", sessionID, GetState(), GetEntriesCount(), MASTERNODE_REJECTED, errorID); return; } else { - LogPrintf("dsa -- is compatible, please submit! \n"); + LogPrintf("dsa -- is compatible, please submit!\n"); pfrom->PushMessage("dssu", sessionID, GetState(), GetEntriesCount(), MASTERNODE_ACCEPTED, errorID); return; } @@ -110,6 +124,15 @@ void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand CDarksendQueue dsq; vRecv >> dsq; + //non-denom's are incompatible + if((dsq.nDenom & (1 << 4))) { + return; + } + + if(dsq.nNumberOfInputs < DARKSEND_MIN_INPUTS || dsq.nNumberOfInputs > DARKSEND_MAX_INPUTS) { + return; + } + CService addr; if(!dsq.GetAddress(addr)) return; if(!dsq.CheckSignature()) return; @@ -173,10 +196,16 @@ void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand } std::vector in; - int64_t nAmount; CTransaction txCollateral; std::vector out; - vRecv >> in >> nAmount >> txCollateral >> out; + vRecv >> in >> txCollateral >> out; + + if((int)in.size() != sessionNumberOfInputs || (int)out.size() != sessionNumberOfInputs) { + errorID = ERR_INVALID_INPUTS_NUMBER; + pfrom->PushMessage("dssu", sessionID, GetState(), GetEntriesCount(), MASTERNODE_REJECTED, errorID); + return; + } + //do we have enough users in the current session? if(!IsSessionReady()){ @@ -270,7 +299,7 @@ void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand } } - if(AddEntry(in, nAmount, txCollateral, out, errorID)){ + if(AddEntry(in, txCollateral, out, errorID)){ pfrom->PushMessage("dssu", sessionID, GetState(), GetEntriesCount(), MASTERNODE_ACCEPTED, errorID); Check(); @@ -886,6 +915,7 @@ void CDarksendPool::CheckForCompleteQueue(){ CDarksendQueue dsq; dsq.nDenom = sessionDenom; + dsq.nNumberOfInputs = sessionNumberOfInputs; dsq.vin = activeMasternode.vin; dsq.time = GetTime(); dsq.ready = true; @@ -992,11 +1022,11 @@ bool CDarksendPool::IsCollateralValid(const CTransaction& txCollateral){ // // Add a clients transaction to the pool // -bool CDarksendPool::AddEntry(const std::vector& newInput, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& newOutput, int& errorID){ +bool CDarksendPool::AddEntry(const std::vector& newInput, const CTransaction& txCollateral, const std::vector& newOutput, int& errorID){ if (!fMasterNode) return false; BOOST_FOREACH(CTxIn in, newInput) { - if (in.prevout.IsNull() || nAmount < 0) { + if (in.prevout.IsNull()) { LogPrint("darksend", "CDarksendPool::AddEntry - input not valid!\n"); errorID = ERR_INVALID_INPUT; sessionUsers--; @@ -1033,7 +1063,7 @@ bool CDarksendPool::AddEntry(const std::vector& newInput, const int64_t& } CDarkSendEntry v; - v.Add(newInput, nAmount, txCollateral, newOutput); + v.Add(newInput, txCollateral, newOutput); entries.push_back(v); LogPrint("darksend", "CDarksendPool::AddEntry -- adding %s\n", newInput[0].ToString()); @@ -1094,7 +1124,7 @@ bool CDarksendPool::SignaturesComplete(){ // Execute a Darksend denomination via a Masternode. // This is only ran from clients // -void CDarksendPool::SendDarksendDenominate(std::vector& vin, std::vector& vout, int64_t amount){ +void CDarksendPool::SendDarksendDenominate(std::vector& vin, std::vector& vout){ if(fMasterNode) { LogPrintf("CDarksendPool::SendDarksendDenominate() - Darksend from a Masternode is not supported currently.\n"); @@ -1141,13 +1171,10 @@ void CDarksendPool::SendDarksendDenominate(std::vector& vin, std::vector< //check it against the memory pool to make sure it's valid { - int64_t nValueOut = 0; - CValidationState state; CMutableTransaction tx; BOOST_FOREACH(const CTxOut& o, vout){ - nValueOut += o.nValue; tx.vout.push_back(o); } @@ -1174,10 +1201,10 @@ void CDarksendPool::SendDarksendDenominate(std::vector& vin, std::vector< // store our entry for later use CDarkSendEntry e; - e.Add(vin, amount, txCollateral, vout); + e.Add(vin, txCollateral, vout); entries.push_back(e); - RelayIn(entries[0].sev, entries[0].amount, txCollateral, entries[0].vout); + RelayIn(entries[0].sev, txCollateral, entries[0].vout); Check(); } @@ -1525,6 +1552,8 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) //non-denom's are incompatible if((dsq.nDenom & (1 << 4))) continue; + if(dsq.nNumberOfInputs < DARKSEND_MIN_INPUTS || dsq.nNumberOfInputs > DARKSEND_MAX_INPUTS) continue; + //don't reuse Masternodes BOOST_FOREACH(CTxIn usedVin, vecMasternodesUsed){ if(dsq.vin == usedVin) { @@ -1535,11 +1564,16 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) std::vector vTempCoins; std::vector vTempCoins2; // Try to match their denominations if possible - if (!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, nValueMin, nBalanceNeedsAnonymized, vTempCoins, vTempCoins2, nValueIn, 0, nDarksendRounds)){ + if (!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, dsq.nNumberOfInputs, vTempCoins, vTempCoins2, 0, nDarksendRounds)){ LogPrintf("DoAutomaticDenominating - Couldn't match denominations %d\n", dsq.nDenom); continue; } + if ((int)vTempCoins.size() < dsq.nNumberOfInputs) { + LogPrintf("DoAutomaticDenominating - Not enough denominations %d to match queue - %d %d\n", dsq.nDenom, (int)vTempCoins.size(), dsq.nNumberOfInputs); + continue; + } + // connect to Masternode and submit the queue request CNode* pnode = ConnectNode((CAddress)addr, NULL, true); if(pnode != NULL) @@ -1553,9 +1587,10 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) pSubmittedToMasternode = pmn; vecMasternodesUsed.push_back(dsq.vin); sessionDenom = dsq.nDenom; + sessionNumberOfInputs = dsq.nNumberOfInputs; - pnode->PushMessage("dsa", sessionDenom, txCollateral); - LogPrintf("DoAutomaticDenominating --- connected (from queue), sending dsa for %d - %s\n", sessionDenom, pnode->addr.ToString()); + pnode->PushMessage("dsa", sessionDenom, sessionNumberOfInputs, txCollateral); + LogPrintf("DoAutomaticDenominating --- connected (from queue), sending dsa for essionNumberOfInputs %d sessionDenom %d - %s\n", sessionNumberOfInputs, sessionDenom, pnode->addr.ToString()); strAutoDenomResult = _("Mixing in progress..."); dsq.time = 0; //remove node return true; @@ -1600,8 +1635,23 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) while(sessionDenom == 0) sessionDenom = GetDenominationsByAmounts(vecAmounts); - pnode->PushMessage("dsa", sessionDenom, txCollateral); - LogPrintf("DoAutomaticDenominating --- connected, sending dsa for %d\n", sessionDenom); + std::vector vTempCoins; + std::vector vTempCoins2; + if (!pwalletMain->SelectCoinsByDenominations(sessionDenom, rand() % DARKSEND_MAX_INPUTS + 1, vTempCoins, vTempCoins2, 0, nDarksendRounds)){ + // should never really happen + LogPrintf("DoAutomaticDenominating - Couldn't match denominations %d\n", sessionDenom); + continue; + } + + if ((int)vTempCoins.size() < DARKSEND_MIN_INPUTS) { + LogPrintf("DoAutomaticDenominating - Not enough denominations to send %d\n", sessionDenom); + continue; + } + + sessionNumberOfInputs = std::min(DARKSEND_MAX_INPUTS, (int)vTempCoins.size()); + + pnode->PushMessage("dsa", sessionDenom, sessionNumberOfInputs, txCollateral); + LogPrintf("DoAutomaticDenominating --- connected, sending dsa for sessionNumberOfInputs %d sessionDenom %d\n", sessionNumberOfInputs, sessionDenom); strAutoDenomResult = _("Mixing in progress..."); return true; } else { @@ -1814,16 +1864,18 @@ bool CDarksendPool::IsCompatibleWithEntries(std::vector& vout) LogPrintf(" vout 2 - %s\n", o2.ToString()); */ if(GetDenominations(vout) != GetDenominations(v.vout)) return false; + if(vout.size() != v.vout.size()) return false; } return true; } -bool CDarksendPool::IsCompatibleWithSession(int64_t nDenom, CTransaction txCollateral, int& errorID) +bool CDarksendPool::IsCompatibleWithSession(int64_t nDenom, int nNumberOfInputs, CTransaction txCollateral, int& errorID) { if(nDenom == 0) return false; - LogPrintf("CDarksendPool::IsCompatibleWithSession - sessionDenom %d sessionUsers %d\n", sessionDenom, sessionUsers); + LogPrintf("CDarksendPool::IsCompatibleWithSession - sessionDenom %d sessionUsers %d nDenom %d nNumberOfInputs %d sessionNumberOfInputs %d\n", + sessionDenom, sessionUsers, nDenom, nNumberOfInputs, sessionNumberOfInputs); if (!unitTest && !IsCollateralValid(txCollateral)){ LogPrint("darksend", "CDarksendPool::IsCompatibleWithSession - collateral not valid!\n"); @@ -1837,12 +1889,14 @@ bool CDarksendPool::IsCompatibleWithSession(int64_t nDenom, CTransaction txColla sessionID = 1 + (rand() % 999999); sessionDenom = nDenom; sessionUsers++; + sessionNumberOfInputs = nNumberOfInputs; lastTimeChanged = GetTimeMillis(); if(!unitTest){ //broadcast that I'm accepting entries, only if it's the first entry through CDarksendQueue dsq; dsq.nDenom = nDenom; + dsq.nNumberOfInputs = nNumberOfInputs; dsq.vin = activeMasternode.vin; dsq.time = GetTime(); dsq.Sign(); @@ -1866,6 +1920,11 @@ bool CDarksendPool::IsCompatibleWithSession(int64_t nDenom, CTransaction txColla return false; } + if(sessionNumberOfInputs != nNumberOfInputs) { + errorID = ERR_INVALID_INPUTS_NUMBER; + return false; + } + LogPrintf("CDarkSendPool::IsCompatibleWithSession - compatible\n"); sessionUsers++; @@ -2012,7 +2071,9 @@ std::string CDarksendPool::GetMessageByID(int messageID) { case ERR_EXISTING_TX: return _("Not compatible with existing transactions."); case ERR_FEES: return _("Transaction fees are too high."); case ERR_INVALID_COLLATERAL: return _("Collateral not valid."); + case ERR_INVALID_DENOM: return _("Invalid denominations."); case ERR_INVALID_INPUT: return _("Input is not valid."); + case ERR_INVALID_INPUTS_NUMBER: return _("Number of inputs is not valid."); case ERR_INVALID_SCRIPT: return _("Invalid script detected."); case ERR_INVALID_TX: return _("Transaction not valid."); case ERR_MAXIMUM: return _("Value more than Darksend pool maximum allows."); @@ -2167,7 +2228,7 @@ void CDarksendPool::RelayFinalTransaction(const int sessionID, const CTransactio } } -void CDarksendPool::RelayIn(const std::vector& vin, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& vout) +void CDarksendPool::RelayIn(const std::vector& vin, const CTransaction& txCollateral, const std::vector& vout) { if(!pSubmittedToMasternode) return; @@ -2183,7 +2244,7 @@ void CDarksendPool::RelayIn(const std::vector& vin, const int64_t& nAmo CNode* pnode = FindNode(pSubmittedToMasternode->addr); if(pnode != NULL) { LogPrintf("RelayIn - found master, relaying message - %s \n", pnode->addr.ToString()); - pnode->PushMessage("dsi", vin2, nAmount, txCollateral, vout2); + pnode->PushMessage("dsi", vin2, txCollateral, vout2); } } diff --git a/src/darksend.h b/src/darksend.h index f0a439e139934..ec62fe290cda3 100644 --- a/src/darksend.h +++ b/src/darksend.h @@ -46,6 +46,9 @@ class CActiveMasternode; #define DARKSEND_RELAY_OUT 2 #define DARKSEND_RELAY_SIG 3 +#define DARKSEND_MIN_INPUTS 1 +#define DARKSEND_MAX_INPUTS 5 + static const int64_t DARKSEND_COLLATERAL = (0.01*COIN); static const int64_t DARKSEND_POOL_MAX = (999.99*COIN); @@ -98,7 +101,6 @@ class CDarkSendEntry bool isSet; std::vector sev; std::vector vout; - int64_t amount; CTransaction collateral; CTransaction txSupporting; int64_t addedTime; // time in UTC milliseconds @@ -107,11 +109,10 @@ class CDarkSendEntry { isSet = false; collateral = CTransaction(); - amount = 0; } /// Add entries to use for Darksend - bool Add(const std::vector vinIn, int64_t amountIn, const CTransaction collateralIn, const std::vector voutIn) + bool Add(const std::vector vinIn, const CTransaction collateralIn, const std::vector voutIn) { if(isSet){return false;} @@ -121,7 +122,6 @@ class CDarkSendEntry BOOST_FOREACH(const CTxOut& out, voutIn) vout.push_back(out); - amount = amountIn; collateral = collateralIn; isSet = true; addedTime = GetTime(); @@ -161,12 +161,14 @@ class CDarksendQueue CTxIn vin; int64_t time; int nDenom; + int nNumberOfInputs; bool ready; //ready for submit std::vector vchSig; CDarksendQueue() { nDenom = 0; + nNumberOfInputs = 0; vin = CTxIn(); time = 0; vchSig.clear(); @@ -178,6 +180,7 @@ class CDarksendQueue template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(nDenom); + READWRITE(nNumberOfInputs); READWRITE(vin); READWRITE(time); READWRITE(ready); @@ -301,7 +304,9 @@ class CDarksendPool ERR_EXISTING_TX, ERR_FEES, ERR_INVALID_COLLATERAL, + ERR_INVALID_DENOM, ERR_INVALID_INPUT, + ERR_INVALID_INPUTS_NUMBER, ERR_INVALID_SCRIPT, ERR_INVALID_TX, ERR_MAXIMUM, @@ -324,6 +329,7 @@ class CDarksendPool CMasternode* pSubmittedToMasternode; int sessionDenom; //Users must submit an denom matching this + int sessionNumberOfInputs;//Users must submit only matching number of inputs int cachedNumBlocks; //used for the overview screen CDarksendPool() @@ -434,7 +440,7 @@ class CDarksendPool bool IsCompatibleWithEntries(std::vector& vout); /// Is this amount compatible with other client in the pool? - bool IsCompatibleWithSession(int64_t nAmount, CTransaction txCollateral, int &errorID); + bool IsCompatibleWithSession(int64_t nAmount, int nNumberOfInputs, CTransaction txCollateral, int &errorID); /// Passively run Darksend in the background according to the configuration in settings (only for QT) bool DoAutomaticDenominating(bool fDryRun=false); @@ -454,13 +460,13 @@ class CDarksendPool /// If the collateral is valid given by a client bool IsCollateralValid(const CTransaction& txCollateral); /// Add a clients entry to the pool - bool AddEntry(const std::vector& newInput, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& newOutput, int& errorID); + bool AddEntry(const std::vector& newInput, const CTransaction& txCollateral, const std::vector& newOutput, int& errorID); /// Add signature to a vin bool AddScriptSig(const CTxIn& newVin); /// Check that all inputs are signed. (Are all inputs signed?) bool SignaturesComplete(); /// As a client, send a transaction to a Masternode to start the denomination process - void SendDarksendDenominate(std::vector& vin, std::vector& vout, int64_t amount); + void SendDarksendDenominate(std::vector& vin, std::vector& vout); /// Get Masternode updates about the progress of Darksend bool StatusUpdate(int newState, int newEntriesCount, int newAccepted, int &errorID, int newSessionID=0); @@ -499,7 +505,7 @@ class CDarksendPool void RelayFinalTransaction(const int sessionID, const CTransaction& txNew); void RelaySignaturesAnon(std::vector& vin); void RelayInAnon(std::vector& vin, std::vector& vout); - void RelayIn(const std::vector& vin, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& vout); + void RelayIn(const std::vector& vin, const CTransaction& txCollateral, const std::vector& vout); void RelayStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const int errorID=MSG_NOERR); void RelayCompletedTransaction(const int sessionID, const bool error, const int errorID); }; diff --git a/src/version.h b/src/version.h index 12f3cceeaf62e..4522dd2f401fd 100644 --- a/src/version.h +++ b/src/version.h @@ -10,7 +10,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 70103; +static const int PROTOCOL_VERSION = 70104; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -22,7 +22,7 @@ static const int GETHEADERS_VERSION = 70077; static const int MIN_PEER_PROTO_VERSION = 70066; //! minimum peer version accepted by DarksendPool -static const int MIN_POOL_PEER_PROTO_VERSION = 70103; +static const int MIN_POOL_PEER_PROTO_VERSION = 70104; //! minimum peer version for masternode budgets static const int MIN_BUDGET_PEER_PROTO_VERSION = 70103; diff --git a/src/wallet.cpp b/src/wallet.cpp index b6293891f3d91..25cda73771b8f 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1787,11 +1787,9 @@ struct CompareByPriority } }; -bool CWallet::SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t nValueMax, std::vector& vCoinsRet, std::vector& vCoinsRet2, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax) +bool CWallet::SelectCoinsByDenominations(int nDenom, int nNumberOfInputs, std::vector& vCoinsRet, std::vector& vCoinsRet2, int nDarksendRoundsMin, int nDarksendRoundsMax) { vCoinsRet.clear(); - nValueRet = 0; - vCoinsRet2.clear(); vector vCoins; AvailableCoins(vCoins, true, NULL, ONLY_DENOMINATED); @@ -1814,53 +1812,44 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t { // 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; - - // Function returns as follows: - // - // bit 0 - 100DRK+1 ( bit on if present ) - // bit 1 - 10DRK+1 - // bit 2 - 1DRK+1 - // bit 3 - .1DRK+1 - - CTxIn vin = CTxIn(out.tx->GetHash(),out.i); - - int rounds = GetInputDarksendRounds(vin); - if(rounds >= nDarksendRoundsMax) continue; - if(rounds < nDarksendRoundsMin) 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)vCoinsRet.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; - - vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey - nValueRet += out.tx->vout[out.i].nValue; - vCoinsRet.push_back(vin); - vCoinsRet2.push_back(out); + bool fAccepted = false; + + // Function returns as follows: + // + // bit 0 - 100DRK+1 ( bit on if present ) + // bit 1 - 10DRK+1 + // bit 2 - 1DRK+1 + // bit 3 - .1DRK+1 + + CTxIn vin = CTxIn(out.tx->GetHash(),out.i); + + int rounds = GetInputDarksendRounds(vin); + if(rounds >= nDarksendRoundsMax) continue; + if(rounds < nDarksendRoundsMin) continue; + + if(fFound100 && fFound10 && fFound1 && fFoundDot1){ //if fulfilled + //we can return this for submission + if((int)vCoinsRet.size() >= nNumberOfInputs) 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; + + vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + vCoinsRet.push_back(vin); + vCoinsRet2.push_back(out); } - return (nValueRet >= nValueMin && fFound100 && fFound10 && fFound1 && fFoundDot1); + return ((int)vCoinsRet.size() >= nNumberOfInputs && fFound100 && fFound10 && fFound1 && fFoundDot1); } bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& setCoinsRet, CAmount& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax) const @@ -2391,7 +2380,7 @@ int64_t CWallet::GetTotalValue(std::vector vCoins) { return nTotalValue; } -string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) +std::string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) { if (IsLocked()) return _("Error: Wallet locked, unable to create transaction!"); @@ -2404,7 +2393,6 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) std::vector vCoins; std::vector vCoinsResult; std::vector vCoins2; - int64_t nValueIn = 0; CReserveKey reservekey(this); /* @@ -2413,11 +2401,17 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) if minRounds >= 0 it means only denominated inputs are going in and coming out */ if(minRounds >= 0){ - if (!SelectCoinsByDenominations(darkSendPool.sessionDenom, 0.1*COIN, DARKSEND_POOL_MAX, vCoins, vCoins2, nValueIn, minRounds, maxRounds)) + if (!SelectCoinsByDenominations(darkSendPool.sessionDenom, darkSendPool.sessionNumberOfInputs, vCoins, vCoins2, minRounds, maxRounds)) return _("Error: Can't select current denominated inputs"); + + if ((int)vCoins.size() < darkSendPool.sessionNumberOfInputs) { + LogPrintf("PrepareDarksendDenominate - Not enough denominations %d to match queue - %d %d\n", darkSendPool.sessionDenom, (int)vCoins.size(), darkSendPool.sessionNumberOfInputs); + return "Error: Not enough denominations to match queue"; + } + } - LogPrintf("PrepareDarksendDenominate - preparing darksend denominate . Got: %d \n", nValueIn); + LogPrintf("PrepareDarksendDenominate - preparing darksend denominate\n"); { LOCK(cs_wallet); @@ -2425,20 +2419,18 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) LockCoin(v.prevout); } - int64_t nValueLeft = nValueIn; std::vector vOut; /* 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. + // Make outputs by looping through denominations: try to add needed denomination, + // repeat darkSendPool.sessionNumberOfInputs 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) { + while(nStep < darkSendPool.sessionNumberOfInputs) { BOOST_FOREACH(int64_t v, darkSendDenominations){ // only use the ones that are approved @@ -2450,7 +2442,7 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) if(!fAccepted) continue; // try to add it - if(nValueLeft - v >= 0) { + if((int)vCoinsResult.size() < darkSendPool.sessionNumberOfInputs) { // Note: this relies on a fact that both vectors MUST have same size std::vector::iterator it = vCoins.begin(); std::vector::iterator it2 = vCoins2.begin(); @@ -2475,7 +2467,6 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) vOut.push_back(o); // subtract denomination amount - nValueLeft -= v; break; } @@ -2484,10 +2475,7 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) } } } - nStep++; - - if(nValueLeft == 0) break; } { @@ -2497,7 +2485,9 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) UnlockCoin(v.prevout); } - if(darkSendPool.GetDenominations(vOut) != darkSendPool.sessionDenom) { + if(darkSendPool.GetDenominations(vOut) != darkSendPool.sessionDenom || + (int)vCoinsResult.size() != darkSendPool.sessionNumberOfInputs || + (int)vOut.size() != darkSendPool.sessionNumberOfInputs) { // unlock used coins on failure LOCK(cs_wallet); BOOST_FOREACH(CTxIn v, vCoinsResult) @@ -2509,7 +2499,7 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) std::random_shuffle (vOut.begin(), vOut.end()); // We also do not care about full amount as long as we have right denominations, just pass what we found - darkSendPool.SendDarksendDenominate(vCoinsResult, vOut, nValueIn - nValueLeft); + darkSendPool.SendDarksendDenominate(vCoinsResult, vOut); return ""; } diff --git a/src/wallet.h b/src/wallet.h index 069b50520e98c..4ad2e1a84dace 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -149,7 +149,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface public: // bool SelectCoins(int64_t nTargetValue, std::set >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS, bool useIX = true) const; bool SelectCoinsDark(int64_t nValueMin, int64_t nValueMax, std::vector& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax) const; - bool SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t nValueMax, std::vector& vCoinsRet, std::vector& vCoinsRet2, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax); + bool SelectCoinsByDenominations(int nDenom, int nNumberOfInputs, std::vector& vCoinsRet, std::vector& vCoinsRet2, int nDarksendRoundsMin, int nDarksendRoundsMax); bool SelectCoinsDarkDenominated(int64_t nTargetValue, std::vector& setCoinsRet, int64_t& nValueRet) const; bool HasCollateralInputs(bool fOnlyConfirmed = true) const; bool IsCollateralAmount(int64_t nInputAmount) const;