diff --git a/src/darksend.cpp b/src/darksend.cpp index b29a301859..c8c477d505 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -109,7 +109,7 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS if(nState == POOL_STATE_QUEUE) { LogPrint("privatesend", "CDarksendPool::ProcessMessage -- DSQUEUE -- PrivateSend queue (%s) is ready on masternode %s\n", dsq.ToString(), addr.ToString()); - PrepareDenominate(); + SubmitDenominate(); } } else { BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue) @@ -1038,16 +1038,16 @@ bool CDarksendPool::IsSignaturesComplete() // Execute a mixing denomination via a Masternode. // This is only ran from clients // -void CDarksendPool::SendDarksendDenominate(const std::vector& vecTxIn, const std::vector& vecTxOut, CAmount nAmount) +bool CDarksendPool::SendDenominate(const std::vector& vecTxIn, const std::vector& vecTxOut) { if(fMasterNode) { - LogPrintf("CDarksendPool::SendDarksendDenominate -- PrivateSend from a Masternode is not supported currently.\n"); - return; + LogPrintf("CDarksendPool::SendDenominate -- PrivateSend from a Masternode is not supported currently.\n"); + return false; } if(txMyCollateral == CMutableTransaction()) { - LogPrintf ("CDarksendPool:SendDarksendDenominate -- PrivateSend collateral not set"); - return; + LogPrintf("CDarksendPool:SendDenominate -- PrivateSend collateral not set\n"); + return false; } // lock the funds we're going to use @@ -1063,24 +1063,24 @@ void CDarksendPool::SendDarksendDenominate(const std::vector& vecTxIn, co // we should already be connected to a Masternode if(!fSessionFoundMasternode) { - LogPrintf("CDarksendPool::SendDarksendDenominate -- No Masternode has been selected yet.\n"); + LogPrintf("CDarksendPool::SendDenominate -- No Masternode has been selected yet.\n"); UnlockCoins(); SetNull(); - return; + return false; } if(!CheckDiskSpace()) { UnlockCoins(); SetNull(); fEnablePrivateSend = false; - LogPrintf("CDarksendPool::SendDarksendDenominate -- Not enough disk space, disabling PrivateSend.\n"); - return; + LogPrintf("CDarksendPool::SendDenominate -- Not enough disk space, disabling PrivateSend.\n"); + return false; } SetState(POOL_STATE_ACCEPTING_ENTRIES); strLastMessage = ""; - LogPrintf("CDarksendPool::SendDarksendDenominate -- Added transaction to pool.\n"); + LogPrintf("CDarksendPool::SendDenominate -- Added transaction to pool.\n"); //check it against the memory pool to make sure it's valid { @@ -1088,24 +1088,24 @@ void CDarksendPool::SendDarksendDenominate(const std::vector& vecTxIn, co CMutableTransaction tx; BOOST_FOREACH(const CTxIn& txin, vecTxIn) { - LogPrint("privatesend", "CDarksendPool::SendDarksendDenominate -- txin=%s\n", txin.ToString()); + LogPrint("privatesend", "CDarksendPool::SendDenominate -- txin=%s\n", txin.ToString()); tx.vin.push_back(txin); } BOOST_FOREACH(const CTxOut& txout, vecTxOut) { - LogPrint("privatesend", "CDarksendPool::SendDarksendDenominate -- txout=%s\n", txout.ToString()); + LogPrint("privatesend", "CDarksendPool::SendDenominate -- txout=%s\n", txout.ToString()); tx.vout.push_back(txout); } - LogPrintf("CDarksendPool::SendDarksendDenominate -- Submitting partial tx %s", tx.ToString()); + LogPrintf("CDarksendPool::SendDenominate -- Submitting partial tx %s", tx.ToString()); mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1*COIN); TRY_LOCK(cs_main, lockMain); if(!lockMain || !AcceptToMemoryPool(mempool, validationState, CTransaction(tx), false, NULL, false, true, true)) { - LogPrintf("CDarksendPool::SendDarksendDenominate -- AcceptToMemoryPool() failed! tx=%s", tx.ToString()); + LogPrintf("CDarksendPool::SendDenominate -- AcceptToMemoryPool() failed! tx=%s", tx.ToString()); UnlockCoins(); SetNull(); - return; + return false; } } @@ -1115,6 +1115,8 @@ void CDarksendPool::SendDarksendDenominate(const std::vector& vecTxIn, co RelayIn(entry); CheckPool(); + + return true; } // Incoming message from Masternode updating the progress of mixing @@ -1624,35 +1626,161 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) return false; } -bool CDarksendPool::PrepareDenominate() +bool CDarksendPool::SubmitDenominate() { std::string strError; + std::vector vecTxInRet; + std::vector vecTxOutRet; // Submit transaction to the pool if we get here // Try to use only inputs with the same number of rounds starting from lowest number of rounds possible for(int i = 0; i < nPrivateSendRounds; i++) { - strError = pwalletMain->PrepareDarksendDenominate(i, i+1); - if(strError == "") { - LogPrintf("CDarksendPool::PrepareDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); - return true; + if(PrepareDenominate(i, i+1, strError, vecTxInRet, vecTxOutRet)) { + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); + return SendDenominate(vecTxInRet, vecTxOutRet); } - LogPrintf("CDarksendPool::PrepareDenominate -- Running PrivateSend denominate for %d rounds. Return '%s'\n", i, strError); + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); } // We failed? That's strange but let's just make final attempt and try to mix everything - strError = pwalletMain->PrepareDarksendDenominate(0, nPrivateSendRounds); - if(strError == "") { - LogPrintf("CDarksendPool::PrepareDenominate -- Running PrivateSend denominate for all rounds, success\n"); - return true; + if(PrepareDenominate(0, nPrivateSendRounds, strError, vecTxInRet, vecTxOutRet)) { + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for all rounds, success\n"); + return SendDenominate(vecTxInRet, vecTxOutRet); } - LogPrintf("CDarksendPool::PrepareDenominate -- Running PrivateSend denominate for all rounds. Return '%s'\n", strError); // Should never actually get here but just in case + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for all rounds, error: %s\n", strError); strAutoDenomResult = strError; - LogPrintf("CDarksendPool::PrepareDenominate -- Error running denominate, %s\n", strError); return false; } +bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector& vecTxInRet, std::vector& vecTxOutRet) +{ + if (pwalletMain->IsLocked()) { + strErrorRet = "Wallet locked, unable to create transaction!"; + return false; + } + + if (GetState() != POOL_STATE_ERROR && GetState() != POOL_STATE_SUCCESS && GetEntriesCount() > 0) { + strErrorRet = "You already have pending entries in the PrivateSend pool"; + return false; + } + + // make sure returning vectors are empty before filling them up + vecTxInRet.clear(); + vecTxOutRet.clear(); + + // ** find the coins we'll use + std::vector vecTxIn; + std::vector vCoins; + CAmount nValueIn = 0; + CReserveKey reservekey(pwalletMain); + + /* + Select the coins we'll use + + 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); + if (nMinRounds >= 0 && !fSelected) { + strErrorRet = "Can't select current denominated inputs"; + return false; + } + + LogPrintf("CDarksendPool::PrepareDenominate -- max value: %f\n", (double)nValueIn/COIN); + + { + LOCK(pwalletMain->cs_wallet); + BOOST_FOREACH(CTxIn txin, vecTxIn) { + pwalletMain->LockCoin(txin.prevout); + } + } + + 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. + // 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) { + + 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; } + + // 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); + + 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); + + // subtract denomination amount + nValueLeft -= nValueDenom; + + break; + } + ++it; + ++it2; + } + } + } + + nStep++; + + if(nValueLeft == 0) break; + } + + { + // unlock unused coins + LOCK(pwalletMain->cs_wallet); + BOOST_FOREACH(CTxIn txin, vecTxIn) { + pwalletMain->UnlockCoin(txin.prevout); + } + } + + if (GetDenominations(vecTxOutRet) != nSessionDenom) { + // unlock used coins on failure + LOCK(pwalletMain->cs_wallet); + BOOST_FOREACH(CTxIn txin, vecTxInRet) { + pwalletMain->UnlockCoin(txin.prevout); + } + strErrorRet = "Can't make current denominated outputs"; + return false; + } + + // We also do not care about full amount as long as we have right denominations + return true; +} + // Create collaterals by looping through inputs grouped by addresses bool CDarksendPool::MakeCollateralAmounts() { diff --git a/src/darksend.h b/src/darksend.h index 92d6199a88..89a28e4e9f 100644 --- a/src/darksend.h +++ b/src/darksend.h @@ -381,11 +381,17 @@ private: /// Create denominations bool CreateDenominated(); bool CreateDenominated(const CompactTallyItem& tallyItem); + /// Split up large inputs or make fee sized inputs bool MakeCollateralAmounts(); bool MakeCollateralAmounts(const CompactTallyItem& tallyItem); - bool PrepareDenominate(); + /// As a client, submit part of a future mixing transaction to a Masternode to start the process + bool SubmitDenominate(); + /// step 1: prepare denominated inputs and outputs + bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector& vecTxInRet, std::vector& vecTxOutRet); + /// step 2: send denominated inputs and outputs prepared in step 1 + bool SendDenominate(const std::vector& vecTxIn, const std::vector& vecTxOut); /// Get Masternode updates about the progress of mixing bool UpdatePoolStateOnClient(int nStateNew, int nEntriesCountNew, int nAcceptedEntriesCountNew, int &nErrorID, int nSessionIDNew=0); @@ -464,8 +470,6 @@ public: /// Do we have enough users to take entries? bool IsSessionReady(){ return nSessionUsers >= GetMaxPoolTransactions(); } - /// As a client, send a transaction to a Masternode to start the denomination process - void SendDarksendDenominate(const std::vector& vecTxIn, const std::vector& vecTxOut, CAmount nAmount); /// Process a new block void NewBlock(); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 08de7dce0b..df1b435e2a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3416,126 +3416,6 @@ CAmount CWallet::GetTotalValue(std::vector vCoins) { return nTotalValue; } -string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds) -{ - if (IsLocked()) - return _("Error: Wallet locked, unable to create transaction!"); - - if(darkSendPool.GetState() != POOL_STATE_ERROR && darkSendPool.GetState() != POOL_STATE_SUCCESS) - if(darkSendPool.GetEntriesCount() > 0) - return _("Error: You already have pending entries in the PrivateSend pool"); - - // ** find the coins we'll use - std::vector vCoins; - std::vector vCoinsResult; - std::vector vCoins2; - CAmount nValueIn = 0; - CReserveKey reservekey(this); - - /* - Select the coins we'll use - - if minRounds >= 0 it means only denominated inputs are going in and coming out - */ - if(minRounds >= 0){ - if (!SelectCoinsByDenominations(darkSendPool.nSessionDenom, 0.1*COIN, PRIVATESEND_POOL_MAX, vCoins, vCoins2, nValueIn, minRounds, maxRounds)) - return _("Error: Can't select current denominated inputs"); - } - - LogPrintf("PrepareDarksendDenominate - preparing PrivateSend denominate . Got: %d \n", nValueIn); - - { - LOCK(cs_wallet); - BOOST_FOREACH(CTxIn v, vCoins) - LockCoin(v.prevout); - } - - CAmount 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. - // 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) { - - BOOST_FOREACH(CAmount v, vecPrivateSendDenominations){ - // only use the ones that are approved - bool fAccepted = false; - if((darkSendPool.nSessionDenom & (1 << 0)) && v == ((100*COIN) +100000)) {fAccepted = true;} - else if((darkSendPool.nSessionDenom & (1 << 1)) && v == ((10*COIN) +10000)) {fAccepted = true;} - else if((darkSendPool.nSessionDenom & (1 << 2)) && v == ((1*COIN) +1000)) {fAccepted = true;} - else if((darkSendPool.nSessionDenom & (1 << 3)) && v == ((.1*COIN) +100)) {fAccepted = true;} - if(!fAccepted) continue; - - // try to add it - if(nValueLeft - v >= 0) { - // 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(); - while(it2 != vCoins2.end()) { - // we have matching inputs - if((*it2).tx->vout[(*it2).i].nValue == v) { - // add new input in resulting vector - vCoinsResult.push_back(*it); - // remove corresponting items from initial vectors - vCoins.erase(it); - vCoins2.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(); - - // add new output - CTxOut o(v, scriptChange); - vOut.push_back(o); - - // subtract denomination amount - nValueLeft -= v; - - break; - } - ++it; - ++it2; - } - } - } - - nStep++; - - if(nValueLeft == 0) break; - } - - { - // unlock unused coins - LOCK(cs_wallet); - BOOST_FOREACH(CTxIn v, vCoins) - UnlockCoin(v.prevout); - } - - if(darkSendPool.GetDenominations(vOut) != darkSendPool.nSessionDenom) { - // unlock used coins on failure - LOCK(cs_wallet); - BOOST_FOREACH(CTxIn v, vCoinsResult) - UnlockCoin(v.prevout); - return "Error: can't make current denominated outputs"; - } - - // 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); - - return ""; -} - DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index db949cd2ad..9cbc092bbd 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -769,7 +769,6 @@ public: std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, AvailableCoinsType coin_type=ALL_COINS, bool fUseInstantSend=false); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand="tx"); - std::string PrepareDarksendDenominate(int minRounds, int maxRounds); int GenerateDarksendOutputs(int nTotalValue, std::vector& vout); bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason); bool ConvertList(std::vector vCoins, std::vector& vecAmounts);