Refactor SendDarksendDenominate, split it into 2 decoupled steps. (#976)

As a client, submit part of a future mixing transaction to a Masternode to start the process (SubmitDenominate):
step 1: prepare denominated inputs and outputs (PrepareDenominate, code moved from wallet.cpp, slightly refactored)
step 2: send denominated inputs and outputs (SendDenominate, slightly refactored)
This commit is contained in:
UdjinM6 2016-09-02 16:19:29 +04:00 committed by GitHub
parent b40c3e5927
commit 199bbf5071
4 changed files with 163 additions and 152 deletions

View File

@ -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<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, CAmount nAmount)
bool CDarksendPool::SendDenominate(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& 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<CTxIn>& 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<CTxIn>& 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<CTxIn>& 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<CTxIn> vecTxInRet;
std::vector<CTxOut> 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<CTxIn>& vecTxInRet, std::vector<CTxOut>& 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<CTxIn> vecTxIn;
std::vector<COutput> 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<CTxIn>::iterator it = vecTxIn.begin();
std::vector<COutput>::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()
{

View File

@ -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<CTxIn>& vecTxInRet, std::vector<CTxOut>& vecTxOutRet);
/// step 2: send denominated inputs and outputs prepared in step 1
bool SendDenominate(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& 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<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, CAmount nAmount);
/// Process a new block
void NewBlock();

View File

@ -3416,126 +3416,6 @@ CAmount CWallet::GetTotalValue(std::vector<CTxIn> 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<CTxIn> vCoins;
std::vector<CTxIn> vCoinsResult;
std::vector<COutput> 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<CTxOut> 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<CTxIn>::iterator it = vCoins.begin();
std::vector<COutput>::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)

View File

@ -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<CTxOut>& vout);
bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason);
bool ConvertList(std::vector<CTxIn> vCoins, std::vector<CAmount>& vecAmounts);