mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 20:42:59 +01:00
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:
parent
b40c3e5927
commit
199bbf5071
184
src/darksend.cpp
184
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<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()
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user