Refactor PS (#1437)

* split CDarksendPool

* split DoAutomaticDenominating

* CMasternode* -> masternode_info_t

* move some globals into CPrivateSendClient

* addressed PR comments
This commit is contained in:
UdjinM6 2017-05-05 14:26:27 +03:00 committed by GitHub
parent 411332f94b
commit 7242e29228
25 changed files with 3051 additions and 2759 deletions

View File

@ -96,9 +96,10 @@ BITCOIN_CORE_H = \
consensus/validation.h \
core_io.h \
core_memusage.h \
darksend.h \
privatesend-client.h \
privatesend.h \
privatesend-server.h \
dsnotificationinterface.h \
darksend-relay.h \
governance.h \
governance-classes.h \
governance-exceptions.h \
@ -220,6 +221,8 @@ libbitcoin_server_a_SOURCES = \
policy/fees.cpp \
policy/policy.cpp \
pow.cpp \
privatesend.cpp \
privatesend-server.cpp \
rest.cpp \
rpcblockchain.cpp \
rpcmasternode.cpp \
@ -257,9 +260,8 @@ libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
activemasternode.cpp \
darksend.cpp \
privatesend-client.cpp \
dsnotificationinterface.cpp \
darksend-relay.cpp \
instantx.cpp \
masternode.cpp \
masternode-payments.cpp \

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "dsnotificationinterface.h"
#include "darksend.h"
#include "privatesend-client.h"
#include "instantx.h"
#include "governance.h"
#include "masternodeman.h"
@ -21,7 +21,7 @@ CDSNotificationInterface::~CDSNotificationInterface()
void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex)
{
mnodeman.UpdatedBlockTip(pindex);
darkSendPool.UpdatedBlockTip(pindex);
privateSendClient.UpdatedBlockTip(pindex);
instantsend.UpdatedBlockTip(pindex);
mnpayments.UpdatedBlockTip(pindex);
governance.UpdatedBlockTip(pindex);

View File

@ -44,7 +44,6 @@
#endif
#include "activemasternode.h"
#include "darksend.h"
#include "dsnotificationinterface.h"
#include "flat-database.h"
#include "governance.h"
@ -58,6 +57,8 @@
#include "masternodeconfig.h"
#include "messagesigner.h"
#include "netfulfilledman.h"
#include "privatesend-client.h"
#include "privatesend-server.h"
#include "spork.h"
#include <stdint.h>
@ -1839,16 +1840,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
nLiquidityProvider = GetArg("-liquidityprovider", nLiquidityProvider);
nLiquidityProvider = std::min(std::max(nLiquidityProvider, 0), 100);
darkSendPool.SetMinBlockSpacing(nLiquidityProvider * 15);
privateSendClient.nLiquidityProvider = std::min(std::max((int)GetArg("-liquidityprovider", DEFAULT_PRIVATESEND_LIQUIDITY), 0), 100);
privateSendClient.SetMinBlockSpacing(privateSendClient.nLiquidityProvider * 15);
fEnablePrivateSend = GetBoolArg("-enableprivatesend", 0);
fPrivateSendMultiSession = GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION);
nPrivateSendRounds = GetArg("-privatesendrounds", DEFAULT_PRIVATESEND_ROUNDS);
nPrivateSendRounds = std::min(std::max(nPrivateSendRounds, 2), nLiquidityProvider ? 99999 : 16);
nPrivateSendAmount = GetArg("-privatesendamount", DEFAULT_PRIVATESEND_AMOUNT);
nPrivateSendAmount = std::min(std::max(nPrivateSendAmount, 2), 999999);
privateSendClient.fEnablePrivateSend = GetBoolArg("-enableprivatesend", false);
privateSendClient.fPrivateSendMultiSession = GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION);
privateSendClient.nPrivateSendRounds = std::min(std::max((int)GetArg("-privatesendrounds", DEFAULT_PRIVATESEND_ROUNDS), 2), privateSendClient.nLiquidityProvider ? 99999 : 16);
privateSendClient.nPrivateSendAmount = std::min(std::max((int)GetArg("-privatesendamount", DEFAULT_PRIVATESEND_AMOUNT), 2), 999999);
fEnableInstantSend = GetBoolArg("-enableinstantsend", 1);
nInstantSendDepth = GetArg("-instantsenddepth", DEFAULT_INSTANTSEND_DEPTH);
@ -1862,10 +1860,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("fLiteMode %d\n", fLiteMode);
LogPrintf("nInstantSendDepth %d\n", nInstantSendDepth);
LogPrintf("PrivateSend rounds %d\n", nPrivateSendRounds);
LogPrintf("PrivateSend amount %d\n", nPrivateSendAmount);
LogPrintf("PrivateSend rounds %d\n", privateSendClient.nPrivateSendRounds);
LogPrintf("PrivateSend amount %d\n", privateSendClient.nPrivateSendAmount);
darkSendPool.InitDenominations();
privateSendClient.InitDenominations();
privateSendServer.InitDenominations();
// ********************************************************* Step 11b: Load cache data
@ -1913,14 +1912,18 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// but don't call it directly to prevent triggering of other listeners like zmq etc.
// GetMainSignals().UpdatedBlockTip(chainActive.Tip());
mnodeman.UpdatedBlockTip(chainActive.Tip());
darkSendPool.UpdatedBlockTip(chainActive.Tip());
privateSendClient.UpdatedBlockTip(chainActive.Tip());
mnpayments.UpdatedBlockTip(chainActive.Tip());
masternodeSync.UpdatedBlockTip(chainActive.Tip());
governance.UpdatedBlockTip(chainActive.Tip());
// ********************************************************* Step 11d: start dash-privatesend thread
// ********************************************************* Step 11d: start dash-ps-<smth> threads
threadGroup.create_thread(boost::bind(&ThreadCheckDarkSendPool));
threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSend));
if (fMasterNode)
threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSendServer));
else
threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSendClient));
// ********************************************************* Step 12: start node

View File

@ -38,12 +38,13 @@
#include "validationinterface.h"
#include "versionbits.h"
#include "darksend.h"
#include "governance.h"
#include "instantx.h"
#include "masternode-payments.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "privatesend-client.h"
#include "privatesend-server.h"
#include <sstream>
@ -6319,7 +6320,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (found)
{
//probably one the extensions
darkSendPool.ProcessMessage(pfrom, strCommand, vRecv);
privateSendClient.ProcessMessage(pfrom, strCommand, vRecv);
privateSendServer.ProcessMessage(pfrom, strCommand, vRecv);
mnodeman.ProcessMessage(pfrom, strCommand, vRecv);
mnpayments.ProcessMessage(pfrom, strCommand, vRecv);
instantsend.ProcessMessage(pfrom, strCommand, vRecv);

View File

@ -4,13 +4,13 @@
#include "activemasternode.h"
#include "addrman.h"
#include "darksend.h"
#include "governance.h"
#include "masternode-payments.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "messagesigner.h"
#include "netfulfilledman.h"
#include "privatesend-client.h"
#include "util.h"
/** Masternode manager */
@ -612,7 +612,7 @@ CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight
return pBestMasternode;
}
CMasternode* CMasternodeMan::FindRandomNotInVec(const std::vector<CTxIn> &vecToExclude, int nProtocolVersion)
masternode_info_t CMasternodeMan::FindRandomNotInVec(const std::vector<CTxIn> &vecToExclude, int nProtocolVersion)
{
LOCK(cs);
@ -622,7 +622,7 @@ CMasternode* CMasternodeMan::FindRandomNotInVec(const std::vector<CTxIn> &vecToE
int nCountNotExcluded = nCountEnabled - vecToExclude.size();
LogPrintf("CMasternodeMan::FindRandomNotInVec -- %d enabled masternodes, %d masternodes to choose from\n", nCountEnabled, nCountNotExcluded);
if(nCountNotExcluded < 1) return NULL;
if(nCountNotExcluded < 1) return masternode_info_t();
// fill a vector of pointers
std::vector<CMasternode*> vpMasternodesShuffled;
@ -648,11 +648,11 @@ CMasternode* CMasternodeMan::FindRandomNotInVec(const std::vector<CTxIn> &vecToE
if(fExclude) continue;
// found the one not in vecToExclude
LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec -- found, masternode=%s\n", pmn->vin.prevout.ToStringShort());
return pmn;
return pmn->GetInfo();
}
LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec -- failed\n");
return NULL;
return masternode_info_t();
}
int CMasternodeMan::GetMasternodeRank(const CTxIn& vin, int nBlockHeight, int nMinProtocol, bool fOnlyActive)
@ -766,7 +766,7 @@ void CMasternodeMan::ProcessMasternodeConnections()
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes) {
if(pnode->fMasternode) {
if(darkSendPool.pSubmittedToMasternode != NULL && pnode->addr == darkSendPool.pSubmittedToMasternode->addr) continue;
if(privateSendClient.infoMixingMasternode.fInfoValid && pnode->addr == privateSendClient.infoMixingMasternode.addr) continue;
LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->id, pnode->addr.ToString());
pnode->fDisconnect = true;
}
@ -1512,6 +1512,18 @@ void CMasternodeMan::UpdateLastPaid()
IsFirstRun = !masternodeSync.IsWinnersListSynced();
}
bool CMasternodeMan::UpdateLastDsq(const CTxIn& vin)
{
masternode_info_t info;
LOCK(cs);
CMasternode* pMN = Find(vin);
if(!pMN)
return false;
pMN->nLastDsq = nDsqCount;
pMN->fAllowMixingTx = true;
return true;
}
void CMasternodeMan::CheckAndRebuildMasternodeIndex()
{
LOCK(cs);

View File

@ -290,7 +290,7 @@ public:
CMasternode* GetNextMasternodeInQueueForPayment(bool fFilterSigTime, int& nCount);
/// Find a random entry
CMasternode* FindRandomNotInVec(const std::vector<CTxIn> &vecToExclude, int nProtocolVersion = -1);
masternode_info_t FindRandomNotInVec(const std::vector<CTxIn> &vecToExclude, int nProtocolVersion = -1);
std::vector<CMasternode> GetFullMasternodeVector() { return vMasternodes; }
@ -322,6 +322,7 @@ public:
bool IsMnbRecoveryRequested(const uint256& hash) { return mMnbRecoveryRequests.count(hash); }
void UpdateLastPaid();
bool UpdateLastDsq(const CTxIn& vin);
void CheckAndRebuildMasternodeIndex();

View File

@ -22,10 +22,10 @@
#include "wallet/wallet.h"
#include "utilstrencodings.h"
#include "darksend.h"
#include "instantx.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "privatesend.h"
#ifdef WIN32
#include <string.h>

1395
src/privatesend-client.cpp Normal file

File diff suppressed because it is too large Load Diff

143
src/privatesend-client.h Normal file
View File

@ -0,0 +1,143 @@
// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef PRIVATESENDCLIENT_H
#define PRIVATESENDCLIENT_H
#include "masternode.h"
#include "privatesend.h"
#include "wallet/wallet.h"
class CPrivateSendClient;
static const int DENOMS_COUNT_MAX = 100;
static const int DEFAULT_PRIVATESEND_ROUNDS = 2;
static const int DEFAULT_PRIVATESEND_AMOUNT = 1000;
static const int DEFAULT_PRIVATESEND_LIQUIDITY = 0;
static const bool DEFAULT_PRIVATESEND_MULTISESSION = false;
// Warn user if mixing in gui or try to create backup if mixing in daemon mode
// when we have only this many keys left
static const int PRIVATESEND_KEYS_THRESHOLD_WARNING = 100;
// Stop mixing completely, it's too dangerous to continue when we have only this many keys left
static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50;
// The main object for accessing mixing
extern CPrivateSendClient privateSendClient;
/** Used to keep track of current status of mixing pool
*/
class CPrivateSendClient : public CPrivateSend
{
private:
mutable CCriticalSection cs_darksend;
// Keep track of the used Masternodes
std::vector<CTxIn> vecMasternodesUsed;
std::vector<CAmount> vecDenominationsSkipped;
std::vector<COutPoint> vecOutPointLocked;
int nCachedLastSuccessBlock;
int nMinBlockSpacing; //required blocks between mixes
const CBlockIndex *pCurrentBlockIndex; // Keep track of current block index
int nEntriesCount;
bool fLastEntryAccepted;
std::string strLastMessage;
std::string strAutoDenomResult;
CMutableTransaction txMyCollateral; // client side collateral
/// Check for process
void CheckPool();
void CompletedTransaction(PoolMessage nMessageID);
bool IsDenomSkipped(CAmount nDenomValue) {
return std::find(vecDenominationsSkipped.begin(), vecDenominationsSkipped.end(), nDenomValue) != vecDenominationsSkipped.end();
}
// Make sure we have enough keys since last backup
bool CheckAutomaticBackup();
bool JoinExistingQueue(CAmount nBalanceNeedsAnonymized);
bool StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsAnonymized);
/// Create denominations
bool CreateDenominated();
bool CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals);
/// Split up large inputs or make fee sized inputs
bool MakeCollateralAmounts();
bool MakeCollateralAmounts(const CompactTallyItem& tallyItem);
/// 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 CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew=0);
// Set the 'state' value, with some logging and capturing when the state changed
void SetState(PoolState nStateNew);
/// As a client, check and sign the final transaction
bool SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode);
void RelayIn(const CDarkSendEntry& entry);
void SetNull();
public:
int nPrivateSendRounds;
int nPrivateSendAmount;
int nLiquidityProvider;
bool fEnablePrivateSend;
bool fPrivateSendMultiSession;
masternode_info_t infoMixingMasternode;
int nCachedNumBlocks; //used for the overview screen
bool fCreateAutoBackups; //builtin support for automatic backups
CPrivateSendClient() :
nCachedLastSuccessBlock(0),
nMinBlockSpacing(0),
txMyCollateral(CMutableTransaction()),
nPrivateSendRounds(DEFAULT_PRIVATESEND_ROUNDS),
nPrivateSendAmount(DEFAULT_PRIVATESEND_AMOUNT),
nLiquidityProvider(DEFAULT_PRIVATESEND_LIQUIDITY),
fEnablePrivateSend(false),
fPrivateSendMultiSession(DEFAULT_PRIVATESEND_MULTISESSION),
nCachedNumBlocks(std::numeric_limits<int>::max()),
fCreateAutoBackups(true) { SetNull(); }
void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
void ClearSkippedDenominations() { vecDenominationsSkipped.clear(); }
void SetMinBlockSpacing(int nMinBlockSpacingIn) { nMinBlockSpacing = nMinBlockSpacingIn; }
void ResetPool();
void UnlockCoins();
std::string GetStatus();
/// Passively run mixing in the background according to the configuration in settings
bool DoAutomaticDenominating(bool fDryRun=false);
void CheckTimeout();
/// Process a new block
void NewBlock();
void UpdatedBlockTip(const CBlockIndex *pindex);
};
void ThreadCheckPrivateSendClient();
#endif

861
src/privatesend-server.cpp Normal file
View File

@ -0,0 +1,861 @@
// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "privatesend-server.h"
#include "activemasternode.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "txmempool.h"
#include "util.h"
#include "utilmoneystr.h"
CPrivateSendServer privateSendServer;
void CPrivateSendServer::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
{
if(!fMasterNode) return;
if(fLiteMode) return; // ignore all Dash related functionality
if(!masternodeSync.IsBlockchainSynced()) return;
if(strCommand == NetMsgType::DSACCEPT) {
if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) {
LogPrintf("DSACCEPT -- incompatible version! nVersion: %d\n", pfrom->nVersion);
PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION);
return;
}
if(IsSessionReady()) {
// too many users in this session already, reject new ones
LogPrintf("DSACCEPT -- queue is already full!\n");
PushStatus(pfrom, STATUS_ACCEPTED, ERR_QUEUE_FULL);
return;
}
int nDenom;
CTransaction txCollateral;
vRecv >> nDenom >> txCollateral;
LogPrint("privatesend", "DSACCEPT -- nDenom %d (%s) txCollateral %s", nDenom, GetDenominationsToString(nDenom), txCollateral.ToString());
CMasternode* pmn = mnodeman.Find(activeMasternode.vin);
if(pmn == NULL) {
PushStatus(pfrom, STATUS_REJECTED, ERR_MN_LIST);
return;
}
if(vecSessionCollaterals.size() == 0 && pmn->nLastDsq != 0 &&
pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5 > mnodeman.nDsqCount)
{
LogPrintf("DSACCEPT -- last dsq too recent, must wait: addr=%s\n", pfrom->addr.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_RECENT);
return;
}
PoolMessage nMessageID = MSG_NOERR;
bool fResult = nSessionID == 0 ? CreateNewSession(nDenom, txCollateral, nMessageID)
: AddUserToExistingSession(nDenom, txCollateral, nMessageID);
if(fResult) {
LogPrintf("DSACCEPT -- is compatible, please submit!\n");
PushStatus(pfrom, STATUS_ACCEPTED, nMessageID);
return;
} else {
LogPrintf("DSACCEPT -- not compatible with existing transactions!\n");
PushStatus(pfrom, STATUS_REJECTED, nMessageID);
return;
}
} else if(strCommand == NetMsgType::DSQUEUE) {
TRY_LOCK(cs_darksend, lockRecv);
if(!lockRecv) return;
if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) {
LogPrint("privatesend", "DSQUEUE -- incompatible version! nVersion: %d\n", pfrom->nVersion);
return;
}
CDarksendQueue dsq;
vRecv >> dsq;
// process every dsq only once
BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue) {
if(q == dsq) {
// LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString());
return;
}
}
LogPrint("privatesend", "DSQUEUE -- %s new\n", dsq.ToString());
if(dsq.IsExpired() || dsq.nTime > GetTime() + PRIVATESEND_QUEUE_TIMEOUT) return;
CMasternode* pmn = mnodeman.Find(dsq.vin);
if(pmn == NULL) return;
if(!dsq.CheckSignature(pmn->pubKeyMasternode)) {
// we probably have outdated info
mnodeman.AskForMN(pfrom, dsq.vin);
return;
}
if(!dsq.fReady) {
BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue) {
if(q.vin == dsq.vin) {
// no way same mn can send another "not yet ready" dsq this soon
LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending WAY too many dsq messages\n", pmn->addr.ToString());
return;
}
}
int nThreshold = pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5;
LogPrint("privatesend", "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", pmn->nLastDsq, nThreshold, mnodeman.nDsqCount);
//don't allow a few nodes to dominate the queuing process
if(pmn->nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) {
LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending too many dsq messages\n", pmn->addr.ToString());
return;
}
mnodeman.nDsqCount++;
pmn->nLastDsq = mnodeman.nDsqCount;
pmn->fAllowMixingTx = true;
LogPrint("privatesend", "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), pmn->addr.ToString());
vecDarksendQueue.push_back(dsq);
dsq.Relay();
}
} else if(strCommand == NetMsgType::DSVIN) {
if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) {
LogPrintf("DSVIN -- incompatible version! nVersion: %d\n", pfrom->nVersion);
PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION);
return;
}
//do we have enough users in the current session?
if(!IsSessionReady()) {
LogPrintf("DSVIN -- session not complete!\n");
PushStatus(pfrom, STATUS_REJECTED, ERR_SESSION);
return;
}
CDarkSendEntry entry;
vRecv >> entry;
LogPrint("privatesend", "DSVIN -- txCollateral %s", entry.txCollateral.ToString());
if(entry.vecTxDSIn.size() > PRIVATESEND_ENTRY_MAX_SIZE) {
LogPrintf("DSVIN -- ERROR: too many inputs! %d/%d\n", entry.vecTxDSIn.size(), PRIVATESEND_ENTRY_MAX_SIZE);
PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM);
return;
}
if(entry.vecTxDSOut.size() > PRIVATESEND_ENTRY_MAX_SIZE) {
LogPrintf("DSVIN -- ERROR: too many outputs! %d/%d\n", entry.vecTxDSOut.size(), PRIVATESEND_ENTRY_MAX_SIZE);
PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM);
return;
}
//do we have the same denominations as the current session?
if(!IsOutputsCompatibleWithSessionDenom(entry.vecTxDSOut)) {
LogPrintf("DSVIN -- not compatible with existing transactions!\n");
PushStatus(pfrom, STATUS_REJECTED, ERR_EXISTING_TX);
return;
}
//check it like a transaction
{
CAmount nValueIn = 0;
CAmount nValueOut = 0;
CMutableTransaction tx;
BOOST_FOREACH(const CTxOut txout, entry.vecTxDSOut) {
nValueOut += txout.nValue;
tx.vout.push_back(txout);
if(txout.scriptPubKey.size() != 25) {
LogPrintf("DSVIN -- non-standard pubkey detected! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
PushStatus(pfrom, STATUS_REJECTED, ERR_NON_STANDARD_PUBKEY);
return;
}
if(!txout.scriptPubKey.IsNormalPaymentScript()) {
LogPrintf("DSVIN -- invalid script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_SCRIPT);
return;
}
}
BOOST_FOREACH(const CTxIn txin, entry.vecTxDSIn) {
tx.vin.push_back(txin);
LogPrint("privatesend", "DSVIN -- txin=%s\n", txin.ToString());
CTransaction txPrev;
uint256 hash;
if(GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) {
if(txPrev.vout.size() > txin.prevout.n)
nValueIn += txPrev.vout[txin.prevout.n].nValue;
} else {
LogPrintf("DSVIN -- missing input! tx=%s", tx.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_MISSING_TX);
return;
}
}
// There should be no fee in mixing tx
CAmount nFee = nValueIn - nValueOut;
if(nFee != 0) {
LogPrintf("DSVIN -- there should be no fee in mixing tx! fees: %lld, tx=%s", nFee, tx.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_FEES);
return;
}
{
LOCK(cs_main);
CValidationState validationState;
mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1*COIN);
if(!AcceptToMemoryPool(mempool, validationState, CTransaction(tx), false, NULL, false, true, true)) {
LogPrintf("DSVIN -- transaction not valid! tx=%s", tx.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_TX);
return;
}
}
}
PoolMessage nMessageID = MSG_NOERR;
if(AddEntry(entry, nMessageID)) {
PushStatus(pfrom, STATUS_ACCEPTED, nMessageID);
CheckPool();
RelayStatus(STATUS_ACCEPTED);
} else {
PushStatus(pfrom, STATUS_REJECTED, nMessageID);
SetNull();
}
} else if(strCommand == NetMsgType::DSSIGNFINALTX) {
if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) {
LogPrintf("DSSIGNFINALTX -- incompatible version! nVersion: %d\n", pfrom->nVersion);
return;
}
std::vector<CTxIn> vecTxIn;
vRecv >> vecTxIn;
LogPrint("privatesend", "DSSIGNFINALTX -- vecTxIn.size() %s\n", vecTxIn.size());
int nTxInIndex = 0;
int nTxInsCount = (int)vecTxIn.size();
BOOST_FOREACH(const CTxIn txin, vecTxIn) {
nTxInIndex++;
if(!AddScriptSig(txin)) {
LogPrint("privatesend", "DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d\n", nTxInIndex, nTxInsCount, nSessionID);
RelayStatus(STATUS_REJECTED);
return;
}
LogPrint("privatesend", "DSSIGNFINALTX -- AddScriptSig() %d/%d success\n", nTxInIndex, nTxInsCount);
}
// all is good
CheckPool();
}
}
void CPrivateSendServer::SetNull()
{
// MN side
vecSessionCollaterals.clear();
CPrivateSend::SetNull();
}
//
// Check the mixing progress and send client updates if a Masternode
//
void CPrivateSendServer::CheckPool()
{
if(fMasterNode) {
LogPrint("privatesend", "CPrivateSendServer::CheckPool -- entries count %lu\n", GetEntriesCount());
// If entries are full, create finalized transaction
if(nState == POOL_STATE_ACCEPTING_ENTRIES && GetEntriesCount() >= GetMaxPoolTransactions()) {
LogPrint("privatesend", "CPrivateSendServer::CheckPool -- FINALIZE TRANSACTIONS\n");
CreateFinalTransaction();
return;
}
// If we have all of the signatures, try to compile the transaction
if(nState == POOL_STATE_SIGNING && IsSignaturesComplete()) {
LogPrint("privatesend", "CPrivateSendServer::CheckPool -- SIGNING\n");
CommitFinalTransaction();
return;
}
}
// reset if we're here for 10 seconds
if((nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) && GetTimeMillis() - nTimeLastSuccessfulStep >= 10000) {
LogPrint("privatesend", "CPrivateSendServer::CheckPool -- timeout, RESETTING\n");
SetNull();
}
}
void CPrivateSendServer::CreateFinalTransaction()
{
LogPrint("privatesend", "CPrivateSendServer::CreateFinalTransaction -- FINALIZE TRANSACTIONS\n");
CMutableTransaction txNew;
// make our new transaction
for(int i = 0; i < GetEntriesCount(); i++) {
BOOST_FOREACH(const CTxDSOut& txdsout, vecEntries[i].vecTxDSOut)
txNew.vout.push_back(txdsout);
BOOST_FOREACH(const CTxDSIn& txdsin, vecEntries[i].vecTxDSIn)
txNew.vin.push_back(txdsin);
}
// BIP69 https://github.com/kristovatlas/bips/blob/master/bip-0069.mediawiki
sort(txNew.vin.begin(), txNew.vin.end());
sort(txNew.vout.begin(), txNew.vout.end());
finalMutableTransaction = txNew;
LogPrint("privatesend", "CPrivateSendServer::CreateFinalTransaction -- finalMutableTransaction=%s", txNew.ToString());
// request signatures from clients
RelayFinalTransaction(finalMutableTransaction);
SetState(POOL_STATE_SIGNING);
}
void CPrivateSendServer::CommitFinalTransaction()
{
if(!fMasterNode) return; // check and relay final tx only on masternode
CTransaction finalTransaction = CTransaction(finalMutableTransaction);
uint256 hashTx = finalTransaction.GetHash();
LogPrint("privatesend", "CPrivateSendServer::CommitFinalTransaction -- finalTransaction=%s", finalTransaction.ToString());
{
// See if the transaction is valid
TRY_LOCK(cs_main, lockMain);
CValidationState validationState;
mempool.PrioritiseTransaction(hashTx, hashTx.ToString(), 1000, 0.1*COIN);
if(!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, false, NULL, false, true, true))
{
LogPrintf("CPrivateSendServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n");
SetNull();
// not much we can do in this case, just notify clients
RelayCompletedTransaction(ERR_INVALID_TX);
return;
}
}
LogPrintf("CPrivateSendServer::CommitFinalTransaction -- CREATING DSTX\n");
// create and sign masternode dstx transaction
if(!mapDarksendBroadcastTxes.count(hashTx)) {
CDarksendBroadcastTx dstx(finalTransaction, activeMasternode.vin, GetAdjustedTime());
dstx.Sign();
mapDarksendBroadcastTxes.insert(std::make_pair(hashTx, dstx));
}
LogPrintf("CPrivateSendServer::CommitFinalTransaction -- TRANSMITTING DSTX\n");
CInv inv(MSG_DSTX, hashTx);
RelayInv(inv);
// Tell the clients it was successful
RelayCompletedTransaction(MSG_SUCCESS);
// Randomly charge clients
ChargeRandomFees();
// Reset
LogPrint("privatesend", "CPrivateSendServer::CommitFinalTransaction -- COMPLETED -- RESETTING\n");
SetNull();
}
//
// Charge clients a fee if they're abusive
//
// Why bother? PrivateSend uses collateral to ensure abuse to the process is kept to a minimum.
// The submission and signing stages are completely separate. In the cases where
// a client submits a transaction then refused to sign, there must be a cost. Otherwise they
// would be able to do this over and over again and bring the mixing to a hault.
//
// How does this work? Messages to Masternodes come in via NetMsgType::DSVIN, these require a valid collateral
// transaction for the client to be able to enter the pool. This transaction is kept by the Masternode
// until the transaction is either complete or fails.
//
void CPrivateSendServer::ChargeFees()
{
if(!fMasterNode) return;
//we don't need to charge collateral for every offence.
if(GetRandInt(100) > 33) return;
std::vector<CTransaction> vecOffendersCollaterals;
if(nState == POOL_STATE_ACCEPTING_ENTRIES) {
BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollaterals) {
bool fFound = false;
BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries)
if(entry.txCollateral == txCollateral)
fFound = true;
// This queue entry didn't send us the promised transaction
if(!fFound) {
LogPrintf("CPrivateSendServer::ChargeFees -- found uncooperative node (didn't send transaction), found offence\n");
vecOffendersCollaterals.push_back(txCollateral);
}
}
}
if(nState == POOL_STATE_SIGNING) {
// who didn't sign?
BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) {
BOOST_FOREACH(const CTxDSIn txdsin, entry.vecTxDSIn) {
if(!txdsin.fHasSig) {
LogPrintf("CPrivateSendServer::ChargeFees -- found uncooperative node (didn't sign), found offence\n");
vecOffendersCollaterals.push_back(entry.txCollateral);
}
}
}
}
// no offences found
if(vecOffendersCollaterals.empty()) return;
//mostly offending? Charge sometimes
if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetRandInt(100) > 33) return;
//everyone is an offender? That's not right
if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions()) return;
//charge one of the offenders randomly
std::random_shuffle(vecOffendersCollaterals.begin(), vecOffendersCollaterals.end());
if(nState == POOL_STATE_ACCEPTING_ENTRIES || nState == POOL_STATE_SIGNING) {
LogPrintf("CPrivateSendServer::ChargeFees -- found uncooperative node (didn't %s transaction), charging fees: %s\n",
(nState == POOL_STATE_SIGNING) ? "sign" : "send", vecOffendersCollaterals[0].ToString());
LOCK(cs_main);
CValidationState state;
bool fMissingInputs;
if(!AcceptToMemoryPool(mempool, state, vecOffendersCollaterals[0], false, &fMissingInputs, false, true)) {
// should never really happen
LogPrintf("CPrivateSendServer::ChargeFees -- ERROR: AcceptToMemoryPool failed!\n");
} else {
RelayTransaction(vecOffendersCollaterals[0]);
}
}
}
/*
Charge the collateral randomly.
Mixing is completely free, to pay miners we randomly pay the collateral of users.
Collateral Fee Charges:
Being that mixing has "no fees" we need to have some kind of cost associated
with using it to stop abuse. Otherwise it could serve as an attack vector and
allow endless transaction that would bloat Dash and make it unusable. To
stop these kinds of attacks 1 in 10 successful transactions are charged. This
adds up to a cost of 0.001DRK per transaction on average.
*/
void CPrivateSendServer::ChargeRandomFees()
{
if(!fMasterNode) return;
LOCK(cs_main);
BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollaterals) {
if(GetRandInt(100) > 10) return;
LogPrintf("CPrivateSendServer::ChargeRandomFees -- charging random fees, txCollateral=%s", txCollateral.ToString());
CValidationState state;
bool fMissingInputs;
if(!AcceptToMemoryPool(mempool, state, txCollateral, false, &fMissingInputs, false, true)) {
// should never really happen
LogPrintf("CPrivateSendServer::ChargeRandomFees -- ERROR: AcceptToMemoryPool failed!\n");
} else {
RelayTransaction(txCollateral);
}
}
}
//
// Check for various timeouts (queue objects, mixing, etc)
//
void CPrivateSendServer::CheckTimeout()
{
{
TRY_LOCK(cs_darksend, lockDS);
if(!lockDS) return; // it's ok to fail here, we run this quite frequently
// check mixing queue objects for timeouts
std::vector<CDarksendQueue>::iterator it = vecDarksendQueue.begin();
while(it != vecDarksendQueue.end()) {
if((*it).IsExpired()) {
LogPrint("privatesend", "CPrivateSendServer::CheckTimeout -- Removing expired queue (%s)\n", (*it).ToString());
it = vecDarksendQueue.erase(it);
} else ++it;
}
}
if(!fMasterNode) return;
int nLagTime = fMasterNode ? 0 : 10000; // if we're the client, give the server a few extra seconds before resetting.
int nTimeout = (nState == POOL_STATE_SIGNING) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT;
bool fTimeout = GetTimeMillis() - nTimeLastSuccessfulStep >= nTimeout*1000 + nLagTime;
if(nState != POOL_STATE_IDLE && fTimeout) {
LogPrint("privatesend", "CPrivateSendServer::CheckTimeout -- %s timed out (%ds) -- restting\n",
(nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout);
ChargeFees();
SetNull();
SetState(POOL_STATE_ERROR);
}
}
/*
Check to see if we're ready for submissions from clients
After receiving multiple dsa messages, the queue will switch to "accepting entries"
which is the active state right before merging the transaction
*/
void CPrivateSendServer::CheckForCompleteQueue()
{
if(!fMasterNode) return;
if(nState == POOL_STATE_QUEUE && IsSessionReady()) {
SetState(POOL_STATE_ACCEPTING_ENTRIES);
CDarksendQueue dsq(nSessionDenom, activeMasternode.vin, GetTime(), true);
LogPrint("privatesend", "CPrivateSendServer::CheckForCompleteQueue -- queue is ready, signing and relaying (%s)\n", dsq.ToString());
dsq.Sign();
dsq.Relay();
}
}
// Check to make sure a given input matches an input in the pool and its scriptSig is valid
bool CPrivateSendServer::IsInputScriptSigValid(const CTxIn& txin)
{
CMutableTransaction txNew;
txNew.vin.clear();
txNew.vout.clear();
int i = 0;
int nTxInIndex = -1;
CScript sigPubKey = CScript();
BOOST_FOREACH(CDarkSendEntry& entry, vecEntries) {
BOOST_FOREACH(const CTxDSOut& txdsout, entry.vecTxDSOut)
txNew.vout.push_back(txdsout);
BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) {
txNew.vin.push_back(txdsin);
if(txdsin.prevout == txin.prevout) {
nTxInIndex = i;
sigPubKey = txdsin.prevPubKey;
}
i++;
}
}
if(nTxInIndex >= 0) { //might have to do this one input at a time?
txNew.vin[nTxInIndex].scriptSig = txin.scriptSig;
LogPrint("privatesend", "CPrivateSendServer::IsInputScriptSigValid -- verifying scriptSig %s\n", ScriptToAsmStr(txin.scriptSig).substr(0,24));
if(!VerifyScript(txNew.vin[nTxInIndex].scriptSig, sigPubKey, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, MutableTransactionSignatureChecker(&txNew, nTxInIndex))) {
LogPrint("privatesend", "CPrivateSendServer::IsInputScriptSigValid -- VerifyScript() failed on input %d\n", nTxInIndex);
return false;
}
} else {
LogPrint("privatesend", "CPrivateSendServer::IsInputScriptSigValid -- Failed to find matching input in pool, %s\n", txin.ToString());
return false;
}
LogPrint("privatesend", "CPrivateSendServer::IsInputScriptSigValid -- Successfully validated input and scriptSig\n");
return true;
}
//
// Add a clients transaction to the pool
//
bool CPrivateSendServer::AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet)
{
if(!fMasterNode) return false;
BOOST_FOREACH(CTxIn txin, entryNew.vecTxDSIn) {
if(txin.prevout.IsNull()) {
LogPrint("privatesend", "CPrivateSendServer::AddEntry -- input not valid!\n");
nMessageIDRet = ERR_INVALID_INPUT;
return false;
}
}
if(!IsCollateralValid(entryNew.txCollateral)) {
LogPrint("privatesend", "CPrivateSendServer::AddEntry -- collateral not valid!\n");
nMessageIDRet = ERR_INVALID_COLLATERAL;
return false;
}
if(GetEntriesCount() >= GetMaxPoolTransactions()) {
LogPrint("privatesend", "CPrivateSendServer::AddEntry -- entries is full!\n");
nMessageIDRet = ERR_ENTRIES_FULL;
return false;
}
BOOST_FOREACH(CTxIn txin, entryNew.vecTxDSIn) {
LogPrint("privatesend", "looking for txin -- %s\n", txin.ToString());
BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) {
BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) {
if(txdsin.prevout == txin.prevout) {
LogPrint("privatesend", "CPrivateSendServer::AddEntry -- found in txin\n");
nMessageIDRet = ERR_ALREADY_HAVE;
return false;
}
}
}
}
vecEntries.push_back(entryNew);
LogPrint("privatesend", "CPrivateSendServer::AddEntry -- adding entry\n");
nMessageIDRet = MSG_ENTRIES_ADDED;
nTimeLastSuccessfulStep = GetTimeMillis();
return true;
}
bool CPrivateSendServer::AddScriptSig(const CTxIn& txinNew)
{
LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24));
BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) {
BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) {
if(txdsin.scriptSig == txinNew.scriptSig) {
LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- already exists\n");
return false;
}
}
}
if(!IsInputScriptSigValid(txinNew)) {
LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- Invalid scriptSig\n");
return false;
}
LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- scriptSig=%s new\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24));
BOOST_FOREACH(CTxIn& txin, finalMutableTransaction.vin) {
if(txinNew.prevout == txin.prevout && txin.nSequence == txinNew.nSequence) {
txin.scriptSig = txinNew.scriptSig;
txin.prevPubKey = txinNew.prevPubKey;
LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24));
}
}
for(int i = 0; i < GetEntriesCount(); i++) {
if(vecEntries[i].AddScriptSig(txinNew)) {
LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- adding to entries, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24));
return true;
}
}
LogPrintf("CPrivateSendServer::AddScriptSig -- Couldn't set sig!\n" );
return false;
}
// Check to make sure everything is signed
bool CPrivateSendServer::IsSignaturesComplete()
{
BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries)
BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn)
if(!txdsin.fHasSig) return false;
return true;
}
bool CPrivateSendServer::IsOutputsCompatibleWithSessionDenom(const std::vector<CTxDSOut>& vecTxDSOut)
{
if(GetDenominations(vecTxDSOut) == 0) return false;
BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) {
LogPrintf("CPrivateSendServer::IsOutputsCompatibleWithSessionDenom -- vecTxDSOut denom %d, entry.vecTxDSOut denom %d\n", GetDenominations(vecTxDSOut), GetDenominations(entry.vecTxDSOut));
if(GetDenominations(vecTxDSOut) != GetDenominations(entry.vecTxDSOut)) return false;
}
return true;
}
bool CPrivateSendServer::IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet)
{
if(!fMasterNode) return false;
// is denom even smth legit?
std::vector<int> vecBits;
if(!GetDenominationsBits(nDenom, vecBits)) {
LogPrint("privatesend", "CPrivateSendServer::IsAcceptableDenomAndCollateral -- denom not valid!\n");
nMessageIDRet = ERR_DENOM;
return false;
}
// check collateral
if(!fUnitTest && !IsCollateralValid(txCollateral)) {
LogPrint("privatesend", "CPrivateSendServer::IsAcceptableDenomAndCollateral -- collateral not valid!\n");
nMessageIDRet = ERR_INVALID_COLLATERAL;
return false;
}
return true;
}
bool CPrivateSendServer::CreateNewSession(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet)
{
if(!fMasterNode || nSessionID != 0) return false;
// new session can only be started in idle mode
if(nState != POOL_STATE_IDLE) {
nMessageIDRet = ERR_MODE;
LogPrintf("CPrivateSendServer::CreateNewSession -- incompatible mode: nState=%d\n", nState);
return false;
}
if(!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) {
return false;
}
// start new session
nMessageIDRet = MSG_NOERR;
nSessionID = GetRandInt(999999)+1;
nSessionDenom = nDenom;
SetState(POOL_STATE_QUEUE);
nTimeLastSuccessfulStep = GetTimeMillis();
if(!fUnitTest) {
//broadcast that I'm accepting entries, only if it's the first entry through
CDarksendQueue dsq(nDenom, activeMasternode.vin, GetTime(), false);
LogPrint("privatesend", "CPrivateSendServer::CreateNewSession -- signing and relaying new queue: %s\n", dsq.ToString());
dsq.Sign();
dsq.Relay();
vecDarksendQueue.push_back(dsq);
}
vecSessionCollaterals.push_back(txCollateral);
LogPrintf("CPrivateSendServer::CreateNewSession -- new session created, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n",
nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size());
return true;
}
bool CPrivateSendServer::AddUserToExistingSession(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet)
{
if(!fMasterNode || nSessionID == 0 || IsSessionReady()) return false;
if(!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) {
return false;
}
// we only add new users to an existing session when we are in queue mode
if(nState != POOL_STATE_QUEUE) {
nMessageIDRet = ERR_MODE;
LogPrintf("CPrivateSendServer::AddUserToExistingSession -- incompatible mode: nState=%d\n", nState);
return false;
}
if(nDenom != nSessionDenom) {
LogPrintf("CPrivateSendServer::AddUserToExistingSession -- incompatible denom %d (%s) != nSessionDenom %d (%s)\n",
nDenom, GetDenominationsToString(nDenom), nSessionDenom, GetDenominationsToString(nSessionDenom));
nMessageIDRet = ERR_DENOM;
return false;
}
// count new user as accepted to an existing session
nMessageIDRet = MSG_NOERR;
nTimeLastSuccessfulStep = GetTimeMillis();
vecSessionCollaterals.push_back(txCollateral);
LogPrintf("CPrivateSendServer::AddUserToExistingSession -- new user accepted, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n",
nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size());
return true;
}
void CPrivateSendServer::RelayFinalTransaction(const CTransaction& txFinal)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION)
pnode->PushMessage(NetMsgType::DSFINALTX, nSessionID, txFinal);
}
void CPrivateSendServer::PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID)
{
if(!pnode) return;
pnode->PushMessage(NetMsgType::DSSTATUSUPDATE, nSessionID, (int)nState, (int)vecEntries.size(), (int)nStatusUpdate, (int)nMessageID);
}
void CPrivateSendServer::RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION)
PushStatus(pnode, nStatusUpdate, nMessageID);
}
void CPrivateSendServer::RelayCompletedTransaction(PoolMessage nMessageID)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION)
pnode->PushMessage(NetMsgType::DSCOMPLETE, nSessionID, (int)nMessageID);
}
void CPrivateSendServer::SetState(PoolState nStateNew)
{
if(fMasterNode && (nStateNew == POOL_STATE_ERROR || nStateNew == POOL_STATE_SUCCESS)) {
LogPrint("privatesend", "CPrivateSendServer::SetState -- Can't set state to ERROR or SUCCESS as a Masternode. \n");
return;
}
LogPrintf("CPrivateSendServer::SetState -- nState: %d, nStateNew: %d\n", nState, nStateNew);
nState = nStateNew;
}
//TODO: Rename/move to core
void ThreadCheckPrivateSendServer()
{
if(fLiteMode) return; // disable all Dash specific functionality
static bool fOneThread;
if(fOneThread) return;
fOneThread = true;
// Make this thread recognisable as the PrivateSend thread
RenameThread("dash-ps-server");
unsigned int nTick = 0;
while (true)
{
MilliSleep(1000);
if(masternodeSync.IsBlockchainSynced() && !ShutdownRequested()) {
nTick++;
privateSendServer.CheckTimeout();
privateSendServer.CheckForCompleteQueue();
}
}
}

82
src/privatesend-server.h Normal file
View File

@ -0,0 +1,82 @@
// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef PRIVATESENDSERVER_H
#define PRIVATESENDSERVER_H
#include "net.h"
#include "privatesend.h"
class CPrivateSendServer;
// The main object for accessing mixing
extern CPrivateSendServer privateSendServer;
/** Used to keep track of current status of mixing pool
*/
class CPrivateSendServer : public CPrivateSend
{
private:
mutable CCriticalSection cs_darksend;
// Mixing uses collateral transactions to trust parties entering the pool
// to behave honestly. If they don't it takes their money.
std::vector<CTransaction> vecSessionCollaterals;
bool fUnitTest;
/// Add a clients entry to the pool
bool AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet);
/// Add signature to a txin
bool AddScriptSig(const CTxIn& txin);
/// Charge fees to bad actors (Charge clients a fee if they're abusive)
void ChargeFees();
/// Rarely charge fees to pay miners
void ChargeRandomFees();
/// Check for process
void CheckPool();
void CreateFinalTransaction();
void CommitFinalTransaction();
/// Is this nDenom and txCollateral acceptable?
bool IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet);
bool CreateNewSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet);
bool AddUserToExistingSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet);
/// Do we have enough users to take entries?
bool IsSessionReady() { return (int)vecSessionCollaterals.size() >= GetMaxPoolTransactions(); }
/// Check that all inputs are signed. (Are all inputs signed?)
bool IsSignaturesComplete();
/// Check to make sure a given input matches an input in the pool and its scriptSig is valid
bool IsInputScriptSigValid(const CTxIn& txin);
/// Are these outputs compatible with other client in the pool?
bool IsOutputsCompatibleWithSessionDenom(const std::vector<CTxDSOut>& vecTxDSOut);
// Set the 'state' value, with some logging and capturing when the state changed
void SetState(PoolState nStateNew);
/// Relay mixing Messages
void RelayFinalTransaction(const CTransaction& txFinal);
void PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID);
void RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID = MSG_NOERR);
void RelayCompletedTransaction(PoolMessage nMessageID);
void SetNull();
public:
CPrivateSendServer() :
fUnitTest(false) { SetNull(); }
void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
void CheckTimeout();
void CheckForCompleteQueue();
};
void ThreadCheckPrivateSendServer();
#endif

416
src/privatesend.cpp Normal file
View File

@ -0,0 +1,416 @@
// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "privatesend.h"
#include "activemasternode.h"
#include "consensus/validation.h"
#include "governance.h"
#include "init.h"
#include "instantx.h"
#include "masternode-payments.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "messagesigner.h"
#include "script/sign.h"
#include "txmempool.h"
#include "util.h"
#include "utilmoneystr.h"
#include <boost/lexical_cast.hpp>
std::map<uint256, CDarksendBroadcastTx> mapDarksendBroadcastTxes;
std::vector<CAmount> vecPrivateSendDenominations;
CDarkSendEntry::CDarkSendEntry(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, const CTransaction& txCollateral) :
txCollateral(txCollateral)
{
BOOST_FOREACH(CTxIn txin, vecTxIn)
vecTxDSIn.push_back(txin);
BOOST_FOREACH(CTxOut txout, vecTxOut)
vecTxDSOut.push_back(txout);
}
bool CDarkSendEntry::AddScriptSig(const CTxIn& txin)
{
BOOST_FOREACH(CTxDSIn& txdsin, vecTxDSIn) {
if(txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) {
if(txdsin.fHasSig) return false;
txdsin.scriptSig = txin.scriptSig;
txdsin.prevPubKey = txin.prevPubKey;
txdsin.fHasSig = true;
return true;
}
}
return false;
}
void CPrivateSend::InitDenominations()
{
vecPrivateSendDenominations.clear();
/* Denominations
A note about convertability. Within mixing pools, each denomination
is convertable to another.
For example:
1DRK+1000 == (.1DRK+100)*10
10DRK+10000 == (1DRK+1000)*10
*/
/* Disabled
vecPrivateSendDenominations.push_back( (100 * COIN)+100000 );
*/
vecPrivateSendDenominations.push_back( (10 * COIN)+10000 );
vecPrivateSendDenominations.push_back( (1 * COIN)+1000 );
vecPrivateSendDenominations.push_back( (.1 * COIN)+100 );
vecPrivateSendDenominations.push_back( (.01 * COIN)+10 );
/* Disabled till we need them
vecPrivateSendDenominations.push_back( (.001 * COIN)+1 );
*/
}
void CPrivateSend::SetNull()
{
// Both sides
nState = POOL_STATE_IDLE;
nSessionID = 0;
nSessionDenom = 0;
vecEntries.clear();
finalMutableTransaction.vin.clear();
finalMutableTransaction.vout.clear();
nTimeLastSuccessfulStep = GetTimeMillis();
}
std::string CPrivateSend::GetStateString() const
{
switch(nState) {
case POOL_STATE_IDLE: return "IDLE";
case POOL_STATE_QUEUE: return "QUEUE";
case POOL_STATE_ACCEPTING_ENTRIES: return "ACCEPTING_ENTRIES";
case POOL_STATE_SIGNING: return "SIGNING";
case POOL_STATE_ERROR: return "ERROR";
case POOL_STATE_SUCCESS: return "SUCCESS";
default: return "UNKNOWN";
}
}
// check to make sure the collateral provided by the client is valid
bool CPrivateSend::IsCollateralValid(const CTransaction& txCollateral)
{
if(txCollateral.vout.empty()) return false;
if(txCollateral.nLockTime != 0) return false;
CAmount nValueIn = 0;
CAmount nValueOut = 0;
bool fMissingTx = false;
BOOST_FOREACH(const CTxOut txout, txCollateral.vout) {
nValueOut += txout.nValue;
if(!txout.scriptPubKey.IsNormalPaymentScript()) {
LogPrintf ("CPrivateSend::IsCollateralValid -- Invalid Script, txCollateral=%s", txCollateral.ToString());
return false;
}
}
BOOST_FOREACH(const CTxIn txin, txCollateral.vin) {
CTransaction txPrev;
uint256 hash;
if(GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) {
if(txPrev.vout.size() > txin.prevout.n)
nValueIn += txPrev.vout[txin.prevout.n].nValue;
} else {
fMissingTx = true;
}
}
if(fMissingTx) {
LogPrint("privatesend", "CPrivateSend::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString());
return false;
}
//collateral transactions are required to pay out PRIVATESEND_COLLATERAL as a fee to the miners
if(nValueIn - nValueOut < PRIVATESEND_COLLATERAL) {
LogPrint("privatesend", "CPrivateSend::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s", nValueOut - nValueIn, txCollateral.ToString());
return false;
}
LogPrint("privatesend", "CPrivateSend::IsCollateralValid -- %s", txCollateral.ToString());
{
LOCK(cs_main);
CValidationState validationState;
if(!AcceptToMemoryPool(mempool, validationState, txCollateral, false, NULL, false, true, true)) {
LogPrint("privatesend", "CPrivateSend::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n");
return false;
}
}
return true;
}
/* Create a nice string to show the denominations
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
*/
std::string CPrivateSend::GetDenominationsToString(int nDenom)
{
std::string strDenom = "";
int nMaxDenoms = vecPrivateSendDenominations.size();
if(nDenom >= (1 << nMaxDenoms)) {
return "out-of-bounds";
}
for (int i = 0; i < nMaxDenoms; ++i) {
if(nDenom & (1 << i)) {
strDenom += (strDenom.empty() ? "" : "+") + FormatMoney(vecPrivateSendDenominations[i]);
}
}
if(strDenom.empty()) {
return "non-denom";
}
return strDenom;
}
int CPrivateSend::GetDenominations(const std::vector<CTxDSOut>& vecTxDSOut)
{
std::vector<CTxOut> vecTxOut;
BOOST_FOREACH(CTxDSOut out, vecTxDSOut)
vecTxOut.push_back(out);
return GetDenominations(vecTxOut);
}
/* Return a bitshifted integer representing the denominations in this list
Function returns as follows (for 4 denominations):
( bit on if present )
100 - bit 0
10 - bit 1
1 - bit 2
.1 - bit 3
non-denom - 0, all bits off
*/
int CPrivateSend::GetDenominations(const std::vector<CTxOut>& vecTxOut, bool fSingleRandomDenom)
{
std::vector<std::pair<CAmount, int> > vecDenomUsed;
// make a list of denominations, with zero uses
BOOST_FOREACH(CAmount nDenomValue, vecPrivateSendDenominations)
vecDenomUsed.push_back(std::make_pair(nDenomValue, 0));
// look for denominations and update uses to 1
BOOST_FOREACH(CTxOut txout, vecTxOut) {
bool found = false;
BOOST_FOREACH (PAIRTYPE(CAmount, int)& s, vecDenomUsed) {
if(txout.nValue == s.first) {
s.second = 1;
found = true;
}
}
if(!found) return 0;
}
int nDenom = 0;
int c = 0;
// if the denomination is used, shift the bit on
BOOST_FOREACH (PAIRTYPE(CAmount, int)& s, vecDenomUsed) {
int bit = (fSingleRandomDenom ? GetRandInt(2) : 1) & s.second;
nDenom |= bit << c++;
if(fSingleRandomDenom && bit) break; // use just one random denomination
}
return nDenom;
}
bool CPrivateSend::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 CPrivateSend::GetDenominationsByAmounts(const std::vector<CAmount>& vecAmount)
{
CScript scriptTmp = CScript();
std::vector<CTxOut> vecTxOut;
BOOST_REVERSE_FOREACH(CAmount nAmount, vecAmount) {
CTxOut txout(nAmount, scriptTmp);
vecTxOut.push_back(txout);
}
return GetDenominations(vecTxOut, true);
}
std::string CPrivateSend::GetMessageByID(PoolMessage nMessageID)
{
switch (nMessageID) {
case ERR_ALREADY_HAVE: return _("Already have that input.");
case ERR_DENOM: return _("No matching denominations found for mixing.");
case ERR_ENTRIES_FULL: return _("Entries are full.");
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_INPUT: return _("Input is not valid.");
case ERR_INVALID_SCRIPT: return _("Invalid script detected.");
case ERR_INVALID_TX: return _("Transaction not valid.");
case ERR_MAXIMUM: return _("Entry exceeds maximum size.");
case ERR_MN_LIST: return _("Not in the Masternode list.");
case ERR_MODE: return _("Incompatible mode.");
case ERR_NON_STANDARD_PUBKEY: return _("Non-standard public key detected.");
case ERR_NOT_A_MN: return _("This is not a Masternode."); // not used
case ERR_QUEUE_FULL: return _("Masternode queue is full.");
case ERR_RECENT: return _("Last PrivateSend was too recent.");
case ERR_SESSION: return _("Session not complete!");
case ERR_MISSING_TX: return _("Missing input transaction information.");
case ERR_VERSION: return _("Incompatible version.");
case MSG_NOERR: return _("No errors detected.");
case MSG_SUCCESS: return _("Transaction created successfully.");
case MSG_ENTRIES_ADDED: return _("Your entries added successfully.");
default: return _("Unknown response.");
}
}
bool CDarksendQueue::Sign()
{
if(!fMasterNode) return false;
std::string strMessage = vin.ToString() + boost::lexical_cast<std::string>(nDenom) + boost::lexical_cast<std::string>(nTime) + boost::lexical_cast<std::string>(fReady);
if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternode.keyMasternode)) {
LogPrintf("CDarksendQueue::Sign -- SignMessage() failed, %s\n", ToString());
return false;
}
return CheckSignature(activeMasternode.pubKeyMasternode);
}
bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyMasternode)
{
std::string strMessage = vin.ToString() + boost::lexical_cast<std::string>(nDenom) + boost::lexical_cast<std::string>(nTime) + boost::lexical_cast<std::string>(fReady);
std::string strError = "";
if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
LogPrintf("CDarksendQueue::CheckSignature -- Got bad Masternode queue signature: %s; error: %s\n", ToString(), strError);
return false;
}
return true;
}
bool CDarksendQueue::Relay()
{
std::vector<CNode*> vNodesCopy = CopyNodeVector();
BOOST_FOREACH(CNode* pnode, vNodesCopy)
if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION)
pnode->PushMessage(NetMsgType::DSQUEUE, (*this));
ReleaseNodeVector(vNodesCopy);
return true;
}
bool CDarksendBroadcastTx::Sign()
{
if(!fMasterNode) return false;
std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast<std::string>(sigTime);
if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternode.keyMasternode)) {
LogPrintf("CDarksendBroadcastTx::Sign -- SignMessage() failed\n");
return false;
}
return CheckSignature(activeMasternode.pubKeyMasternode);
}
bool CDarksendBroadcastTx::CheckSignature(const CPubKey& pubKeyMasternode)
{
std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast<std::string>(sigTime);
std::string strError = "";
if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
LogPrintf("CDarksendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s\n", strError);
return false;
}
return true;
}
//TODO: Rename/move to core
void ThreadCheckPrivateSend()
{
if(fLiteMode) return; // disable all Dash specific functionality
static bool fOneThread;
if(fOneThread) return;
fOneThread = true;
// Make this thread recognisable as the PrivateSend thread
RenameThread("dash-ps");
unsigned int nTick = 0;
while (true)
{
MilliSleep(1000);
// try to sync from all available nodes, one step at a time
masternodeSync.ProcessTick();
if(masternodeSync.IsBlockchainSynced() && !ShutdownRequested()) {
nTick++;
// make sure to check all masternodes first
mnodeman.Check();
// check if we should activate or ping every few minutes,
// slightly postpone first run to give net thread a chance to connect to some peers
if(nTick % MASTERNODE_MIN_MNP_SECONDS == 15)
activeMasternode.ManageState();
if(nTick % 60 == 0) {
mnodeman.ProcessMasternodeConnections();
mnodeman.CheckAndRemove();
mnpayments.CheckAndRemove();
instantsend.CheckAndRemove();
}
if(fMasterNode && (nTick % (60 * 5) == 0)) {
mnodeman.DoFullVerificationStep();
}
if(nTick % (60 * 5) == 0) {
governance.DoMaintenance();
}
}
}
}

View File

@ -2,14 +2,16 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DARKSEND_H
#define DARKSEND_H
#ifndef PRIVATESEND_H
#define PRIVATESEND_H
#include "masternode.h"
#include "wallet/wallet.h"
#include "chainparams.h"
#include "primitives/transaction.h"
#include "pubkey.h"
#include "utiltime.h"
#include "tinyformat.h"
class CDarksendPool;
class CDarksendBroadcastTx;
class CPrivateSend;
// timeouts
static const int PRIVATESEND_AUTO_TIMEOUT_MIN = 5;
@ -22,27 +24,11 @@ static const int MIN_PRIVATESEND_PEER_PROTO_VERSION = 70206;
static const CAmount PRIVATESEND_COLLATERAL = 0.001 * COIN;
static const CAmount PRIVATESEND_ENTRY_MAX_SIZE = 9;
static const int DENOMS_COUNT_MAX = 100;
static const int DEFAULT_PRIVATESEND_ROUNDS = 2;
static const int DEFAULT_PRIVATESEND_AMOUNT = 1000;
static const int DEFAULT_PRIVATESEND_LIQUIDITY = 0;
static const bool DEFAULT_PRIVATESEND_MULTISESSION = false;
// Warn user if mixing in gui or try to create backup if mixing in daemon mode
// when we have only this many keys left
static const int PRIVATESEND_KEYS_THRESHOLD_WARNING = 100;
// Stop mixing completely, it's too dangerous to continue when we have only this many keys left
static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50;
extern int nPrivateSendRounds;
extern int nPrivateSendAmount;
extern int nLiquidityProvider;
extern bool fEnablePrivateSend;
extern bool fPrivateSendMultiSession;
class CDarksendBroadcastTx;
// The main object for accessing mixing
extern CDarksendPool darkSendPool;
// extern CPrivateSend privateSend;
extern std::map<uint256, CDarksendBroadcastTx> mapDarksendBroadcastTxes;
extern std::vector<CAmount> vecPrivateSendDenominations;
@ -100,14 +86,7 @@ public:
txCollateral(CTransaction())
{}
CDarkSendEntry(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, const CTransaction& txCollateral) :
txCollateral(txCollateral)
{
BOOST_FOREACH(CTxIn txin, vecTxIn)
vecTxDSIn.push_back(txin);
BOOST_FOREACH(CTxOut txout, vecTxOut)
vecTxDSOut.push_back(txout);
}
CDarkSendEntry(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, const CTransaction& txCollateral);
ADD_SERIALIZE_METHODS;
@ -231,11 +210,10 @@ public:
bool CheckSignature(const CPubKey& pubKeyMasternode);
};
/** Used to keep track of current status of mixing pool
*/
class CDarksendPool
// static class or base class or helper?
class CPrivateSend
{
private:
protected:
// pool responses
enum PoolMessage {
ERR_ALREADY_HAVE,
@ -251,7 +229,7 @@ private:
ERR_MN_LIST,
ERR_MODE,
ERR_NON_STANDARD_PUBKEY,
ERR_NOT_A_MN,
ERR_NOT_A_MN, // not used
ERR_QUEUE_FULL,
ERR_RECENT,
ERR_SESSION,
@ -282,58 +260,18 @@ private:
STATUS_ACCEPTED
};
mutable CCriticalSection cs_darksend;
// The current mixing sessions in progress on the network
std::vector<CDarksendQueue> vecDarksendQueue;
// Keep track of the used Masternodes
std::vector<CTxIn> vecMasternodesUsed;
std::vector<CAmount> vecDenominationsSkipped;
std::vector<COutPoint> vecOutPointLocked;
// Mixing uses collateral transactions to trust parties entering the pool
// to behave honestly. If they don't it takes their money.
std::vector<CTransaction> vecSessionCollaterals;
std::vector<CDarkSendEntry> vecEntries; // Masternode/clients entries
PoolState nState; // should be one of the POOL_STATE_XXX values
int64_t nTimeLastSuccessfulStep; // the time when last successful mixing step was performed, in UTC milliseconds
int nCachedLastSuccessBlock;
int nMinBlockSpacing; //required blocks between mixes
const CBlockIndex *pCurrentBlockIndex; // Keep track of current block index
int nSessionID; // 0 if no mixing session is active
int nEntriesCount;
bool fLastEntryAccepted;
std::string strLastMessage;
std::string strAutoDenomResult;
bool fUnitTest;
CMutableTransaction txMyCollateral; // client side collateral
CMutableTransaction finalMutableTransaction; // the finalized transaction ready for signing
/// Add a clients entry to the pool
bool AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet);
/// Add signature to a txin
bool AddScriptSig(const CTxIn& txin);
/// Charge fees to bad actors (Charge clients a fee if they're abusive)
void ChargeFees();
/// Rarely charge fees to pay miners
void ChargeRandomFees();
/// Check for process
void CheckPool();
void CreateFinalTransaction();
void CommitFinalTransaction();
void CompletedTransaction(PoolMessage nMessageID);
/// Get the denominations for a specific amount of dash.
int GetDenominationsByAmounts(const std::vector<CAmount>& vecAmount);
@ -342,92 +280,17 @@ private:
/// Get the maximum number of transactions for the pool
int GetMaxPoolTransactions() { return Params().PoolMaxTransactions(); }
/// Is this nDenom and txCollateral acceptable?
bool IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet);
bool CreateNewSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet);
bool AddUserToExistingSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet);
/// Do we have enough users to take entries?
bool IsSessionReady() { return (int)vecSessionCollaterals.size() >= GetMaxPoolTransactions(); }
/// If the collateral is valid given by a client
bool IsCollateralValid(const CTransaction& txCollateral);
/// Check that all inputs are signed. (Are all inputs signed?)
bool IsSignaturesComplete();
/// Check to make sure a given input matches an input in the pool and its scriptSig is valid
bool IsInputScriptSigValid(const CTxIn& txin);
/// Are these outputs compatible with other client in the pool?
bool IsOutputsCompatibleWithSessionDenom(const std::vector<CTxDSOut>& vecTxDSOut);
bool IsDenomSkipped(CAmount nDenomValue) {
return std::find(vecDenominationsSkipped.begin(), vecDenominationsSkipped.end(), nDenomValue) != vecDenominationsSkipped.end();
}
/// Create denominations
bool CreateDenominated();
bool CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals);
/// Split up large inputs or make fee sized inputs
bool MakeCollateralAmounts();
bool MakeCollateralAmounts(const CompactTallyItem& tallyItem);
/// 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 CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew=0);
// Set the 'state' value, with some logging and capturing when the state changed
void SetState(PoolState nStateNew);
/// As a client, check and sign the final transaction
bool SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode);
/// Relay mixing Messages
void RelayFinalTransaction(const CTransaction& txFinal);
void RelaySignaturesAnon(std::vector<CTxIn>& vin);
void RelayInAnon(std::vector<CTxIn>& vin, std::vector<CTxOut>& vout);
void RelayIn(const CDarkSendEntry& entry);
void PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID);
void RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID = MSG_NOERR);
void RelayCompletedTransaction(PoolMessage nMessageID);
void SetNull();
public:
CMasternode* pSubmittedToMasternode;
int nSessionDenom; //Users must submit an denom matching this
int nCachedNumBlocks; //used for the overview screen
bool fCreateAutoBackups; //builtin support for automatic backups
CDarksendPool() :
nCachedLastSuccessBlock(0),
nMinBlockSpacing(0),
fUnitTest(false),
txMyCollateral(CMutableTransaction()),
nCachedNumBlocks(std::numeric_limits<int>::max()),
fCreateAutoBackups(true) { SetNull(); }
/** Process a mixing message using the protocol below
* \param pfrom
* \param strCommand lower case command string; valid values are:
* Command | Description
* -------- | -----------------
* dsa | Acceptable
* dsc | Complete
* dsf | Final tx
* dsi | Vector of CTxIn
* dsq | Queue
* dss | Signal Final Tx
* dssu | status update
* \param vRecv
*/
void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
CPrivateSend() { SetNull(); }
void InitDenominations();
void ClearSkippedDenominations() { vecDenominationsSkipped.clear(); }
/// Get the denominations for a list of outputs (returns a bitshifted integer)
int GetDenominations(const std::vector<CTxOut>& vecTxOut, bool fSingleRandomDenom = false);
@ -437,31 +300,13 @@ public:
CAmount GetMaxPoolAmount() { return vecPrivateSendDenominations.empty() ? 0 : PRIVATESEND_ENTRY_MAX_SIZE * vecPrivateSendDenominations.front(); }
void SetMinBlockSpacing(int nMinBlockSpacingIn) { nMinBlockSpacing = nMinBlockSpacingIn; }
void ResetPool();
void UnlockCoins();
int GetQueueSize() const { return vecDarksendQueue.size(); }
int GetState() const { return nState; }
std::string GetStateString() const;
std::string GetStatus();
int GetEntriesCount() const { return vecEntries.size(); }
/// Passively run mixing in the background according to the configuration in settings
bool DoAutomaticDenominating(bool fDryRun=false);
void CheckTimeout();
void CheckForCompleteQueue();
/// Process a new block
void NewBlock();
void UpdatedBlockTip(const CBlockIndex *pindex);
};
void ThreadCheckDarkSendPool();
void ThreadCheckPrivateSend();
#endif

View File

@ -18,9 +18,9 @@
#include "ui_interface.h"
#include "util.h"
#include "darksend.h"
#include "masternodeman.h"
#include "masternode-sync.h"
#include "privatesend-client.h"
#include <stdint.h>

View File

@ -19,8 +19,8 @@
#include "main.h" // For minRelayTxFee
#include "wallet/wallet.h"
#include "darksend.h"
#include "instantx.h"
#include "privatesend-client.h"
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
@ -451,7 +451,7 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
coinControl->Select(outpt);
CTxIn txin = CTxIn(outpt);
int nRounds = pwalletMain->GetInputPrivateSendRounds(txin);
if (coinControl->fUsePrivateSend && nRounds < nPrivateSendRounds) {
if (coinControl->fUsePrivateSend && nRounds < privateSendClient.nPrivateSendRounds) {
QMessageBox::warning(this, windowTitle(),
tr("Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again."),
QMessageBox::Ok, QMessageBox::Ok);

View File

@ -2,9 +2,9 @@
#include "ui_darksendconfig.h"
#include "bitcoinunits.h"
#include "darksend.h"
#include "guiconstants.h"
#include "optionsmodel.h"
#include "privatesend-client.h"
#include "walletmodel.h"
#include <QMessageBox>
@ -86,6 +86,6 @@ void DarksendConfig::configure(bool enabled, int coins, int rounds) {
settings.setValue("nPrivateSendRounds", rounds);
settings.setValue("nPrivateSendAmount", coins);
nPrivateSendRounds = rounds;
nPrivateSendAmount = coins;
privateSendClient.nPrivateSendRounds = rounds;
privateSendClient.nPrivateSendAmount = coins;
}

View File

@ -21,7 +21,7 @@
#include "wallet/wallet.h" // for CWallet::GetRequiredFee()
#endif
#include "darksend.h"
#include "privatesend-client.h"
#include <boost/thread.hpp>
@ -260,7 +260,7 @@ void OptionsDialog::on_resetButton_clicked()
void OptionsDialog::on_okButton_clicked()
{
mapper->submit();
darkSendPool.nCachedNumBlocks = std::numeric_limits<int>::max();
privateSendClient.nCachedNumBlocks = std::numeric_limits<int>::max();
pwalletMain->MarkDirty();
accept();
updateDefaultProxyNets();

View File

@ -23,10 +23,10 @@
#include "wallet/walletdb.h"
#endif
#include "darksend.h"
#ifdef ENABLE_WALLET
#include "masternodeconfig.h"
#endif
#include "privatesend-client.h"
#include <QNetworkProxy>
#include <QSettings>
@ -126,7 +126,7 @@ void OptionsModel::Init(bool resetSettings)
settings.setValue("nPrivateSendRounds", DEFAULT_PRIVATESEND_ROUNDS);
if (!SoftSetArg("-privatesendrounds", settings.value("nPrivateSendRounds").toString().toStdString()))
addOverriddenOption("-privatesendrounds");
nPrivateSendRounds = settings.value("nPrivateSendRounds").toInt();
privateSendClient.nPrivateSendRounds = settings.value("nPrivateSendRounds").toInt();
if (!settings.contains("nPrivateSendAmount")) {
// for migration from old settings
@ -137,13 +137,13 @@ void OptionsModel::Init(bool resetSettings)
}
if (!SoftSetArg("-privatesendamount", settings.value("nPrivateSendAmount").toString().toStdString()))
addOverriddenOption("-privatesendamount");
nPrivateSendAmount = settings.value("nPrivateSendAmount").toInt();
privateSendClient.nPrivateSendAmount = settings.value("nPrivateSendAmount").toInt();
if (!settings.contains("fPrivateSendMultiSession"))
settings.setValue("fPrivateSendMultiSession", DEFAULT_PRIVATESEND_MULTISESSION);
if (!SoftSetBoolArg("-privatesendmultisession", settings.value("fPrivateSendMultiSession").toBool()))
addOverriddenOption("-privatesendmultisession");
fPrivateSendMultiSession = settings.value("fPrivateSendMultiSession").toBool();
privateSendClient.fPrivateSendMultiSession = settings.value("fPrivateSendMultiSession").toBool();
#endif
// Network
@ -407,24 +407,24 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
case PrivateSendRounds:
if (settings.value("nPrivateSendRounds") != value)
{
nPrivateSendRounds = value.toInt();
settings.setValue("nPrivateSendRounds", nPrivateSendRounds);
privateSendClient.nPrivateSendRounds = value.toInt();
settings.setValue("nPrivateSendRounds", privateSendClient.nPrivateSendRounds);
Q_EMIT privateSendRoundsChanged();
}
break;
case PrivateSendAmount:
if (settings.value("nPrivateSendAmount") != value)
{
nPrivateSendAmount = value.toInt();
settings.setValue("nPrivateSendAmount", nPrivateSendAmount);
privateSendClient.nPrivateSendAmount = value.toInt();
settings.setValue("nPrivateSendAmount", privateSendClient.nPrivateSendAmount);
Q_EMIT privateSentAmountChanged();
}
break;
case PrivateSendMultiSession:
if (settings.value("fPrivateSendMultiSession") != value)
{
fPrivateSendMultiSession = value.toBool();
settings.setValue("fPrivateSendMultiSession", fPrivateSendMultiSession);
privateSendClient.fPrivateSendMultiSession = value.toBool();
settings.setValue("fPrivateSendMultiSession", privateSendClient.fPrivateSendMultiSession);
}
break;
#endif

View File

@ -18,10 +18,10 @@
#include "utilitydialog.h"
#include "walletmodel.h"
#include "darksend.h"
#include "instantx.h"
#include "darksendconfig.h"
#include "masternode-sync.h"
#include "privatesend-client.h"
#include <QAbstractItemDelegate>
#include <QPainter>
@ -168,14 +168,14 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
ui->labelPrivateSendEnabled->setToolTip(tr("Automatic backups are disabled, no mixing available!"));
}
} else {
if(!fEnablePrivateSend){
if(!privateSendClient.fEnablePrivateSend){
ui->togglePrivateSend->setText(tr("Start Mixing"));
} else {
ui->togglePrivateSend->setText(tr("Stop Mixing"));
}
// Disable darkSendPool builtin support for automatic backups while we are in GUI,
// Disable privateSendClient builtin support for automatic backups while we are in GUI,
// we'll handle automatic backups and user warnings in privateSendStatus()
darkSendPool.fCreateAutoBackups = false;
privateSendClient.fCreateAutoBackups = false;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(privateSendStatus()));
@ -330,7 +330,7 @@ void OverviewPage::updatePrivateSendProgress()
if(!pwalletMain) return;
QString strAmountAndRounds;
QString strPrivateSendAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nPrivateSendAmount * COIN, false, BitcoinUnits::separatorAlways);
QString strPrivateSendAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, privateSendClient.nPrivateSendAmount * COIN, false, BitcoinUnits::separatorAlways);
if(currentBalance == 0)
{
@ -339,7 +339,7 @@ void OverviewPage::updatePrivateSendProgress()
// when balance is zero just show info from settings
strPrivateSendAmount = strPrivateSendAmount.remove(strPrivateSendAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1);
strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", nPrivateSendRounds);
strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", privateSendClient.nPrivateSendRounds);
ui->labelAmountRounds->setToolTip(tr("No inputs detected"));
ui->labelAmountRounds->setText(strAmountAndRounds);
@ -363,15 +363,15 @@ void OverviewPage::updatePrivateSendProgress()
CAmount nMaxToAnonymize = nAnonymizableBalance + currentAnonymizedBalance + nDenominatedUnconfirmedBalance;
// If it's more than the anon threshold, limit to that.
if(nMaxToAnonymize > nPrivateSendAmount*COIN) nMaxToAnonymize = nPrivateSendAmount*COIN;
if(nMaxToAnonymize > privateSendClient.nPrivateSendAmount*COIN) nMaxToAnonymize = privateSendClient.nPrivateSendAmount*COIN;
if(nMaxToAnonymize == 0) return;
if(nMaxToAnonymize >= nPrivateSendAmount * COIN) {
if(nMaxToAnonymize >= privateSendClient.nPrivateSendAmount * COIN) {
ui->labelAmountRounds->setToolTip(tr("Found enough compatible inputs to anonymize %1")
.arg(strPrivateSendAmount));
strPrivateSendAmount = strPrivateSendAmount.remove(strPrivateSendAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1);
strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", nPrivateSendRounds);
strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", privateSendClient.nPrivateSendRounds);
} else {
QString strMaxToAnonymize = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nMaxToAnonymize, false, BitcoinUnits::separatorAlways);
ui->labelAmountRounds->setToolTip(tr("Not enough compatible inputs to anonymize <span style='color:red;'>%1</span>,<br>"
@ -381,7 +381,7 @@ void OverviewPage::updatePrivateSendProgress()
strMaxToAnonymize = strMaxToAnonymize.remove(strMaxToAnonymize.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1);
strAmountAndRounds = "<span style='color:red;'>" +
QString(BitcoinUnits::factor(nDisplayUnit) == 1 ? "" : "~") + strMaxToAnonymize +
" / " + tr("%n Rounds", "", nPrivateSendRounds) + "</span>";
" / " + tr("%n Rounds", "", privateSendClient.nPrivateSendRounds) + "</span>";
}
ui->labelAmountRounds->setText(strAmountAndRounds);
@ -408,7 +408,7 @@ void OverviewPage::updatePrivateSendProgress()
// apply some weights to them ...
float denomWeight = 1;
float anonNormWeight = nPrivateSendRounds;
float anonNormWeight = privateSendClient.nPrivateSendRounds;
float anonFullWeight = 2;
float fullWeight = denomWeight + anonNormWeight + anonFullWeight;
// ... and calculate the whole progress
@ -424,7 +424,7 @@ void OverviewPage::updatePrivateSendProgress()
tr("Denominated") + ": %2%<br/>" +
tr("Mixed") + ": %3%<br/>" +
tr("Anonymized") + ": %4%<br/>" +
tr("Denominated inputs have %5 of %n rounds on average", "", nPrivateSendRounds))
tr("Denominated inputs have %5 of %n rounds on average", "", privateSendClient.nPrivateSendRounds))
.arg(progress).arg(denomPart).arg(anonNormPart).arg(anonFullPart)
.arg(nAverageAnonymizedRounds);
ui->privateSendProgress->setToolTip(strToolPip);
@ -456,7 +456,7 @@ void OverviewPage::privateSendStatus()
int nBestHeight = clientModel->getNumBlocks();
// We are processing more then 1 block per second, we'll just leave
if(((nBestHeight - darkSendPool.nCachedNumBlocks) / (GetTimeMillis() - nLastDSProgressBlockTime + 1) > 1)) return;
if(((nBestHeight - privateSendClient.nCachedNumBlocks) / (GetTimeMillis() - nLastDSProgressBlockTime + 1) > 1)) return;
nLastDSProgressBlockTime = GetTimeMillis();
QString strKeysLeftText(tr("keys left: %1").arg(pwalletMain->nKeysLeftSinceAutoBackup));
@ -465,9 +465,9 @@ void OverviewPage::privateSendStatus()
}
ui->labelPrivateSendEnabled->setToolTip(strKeysLeftText);
if (!fEnablePrivateSend) {
if (nBestHeight != darkSendPool.nCachedNumBlocks) {
darkSendPool.nCachedNumBlocks = nBestHeight;
if (!privateSendClient.fEnablePrivateSend) {
if (nBestHeight != privateSendClient.nCachedNumBlocks) {
privateSendClient.nCachedNumBlocks = nBestHeight;
updatePrivateSendProgress();
}
@ -522,7 +522,7 @@ void OverviewPage::privateSendStatus()
}
}
QString strEnabled = fEnablePrivateSend ? tr("Enabled") : tr("Disabled");
QString strEnabled = privateSendClient.fEnablePrivateSend ? tr("Enabled") : tr("Disabled");
// Show how many keys left in advanced PS UI mode only
if(fShowAdvancedPSUI) strEnabled += ", " + strKeysLeftText;
ui->labelPrivateSendEnabled->setText(strEnabled);
@ -544,13 +544,13 @@ void OverviewPage::privateSendStatus()
}
// check darksend status and unlock if needed
if(nBestHeight != darkSendPool.nCachedNumBlocks) {
if(nBestHeight != privateSendClient.nCachedNumBlocks) {
// Balance and number of transactions might have changed
darkSendPool.nCachedNumBlocks = nBestHeight;
privateSendClient.nCachedNumBlocks = nBestHeight;
updatePrivateSendProgress();
}
QString strStatus = QString(darkSendPool.GetStatus().c_str());
QString strStatus = QString(privateSendClient.GetStatus().c_str());
QString s = tr("Last PrivateSend message:\n") + strStatus;
@ -559,21 +559,21 @@ void OverviewPage::privateSendStatus()
ui->labelPrivateSendLastMessage->setText(s);
if(darkSendPool.nSessionDenom == 0){
if(privateSendClient.nSessionDenom == 0){
ui->labelSubmittedDenom->setText(tr("N/A"));
} else {
QString strDenom(darkSendPool.GetDenominationsToString(darkSendPool.nSessionDenom).c_str());
QString strDenom(privateSendClient.GetDenominationsToString(privateSendClient.nSessionDenom).c_str());
ui->labelSubmittedDenom->setText(strDenom);
}
}
void OverviewPage::privateSendAuto(){
darkSendPool.DoAutomaticDenominating();
privateSendClient.DoAutomaticDenominating();
}
void OverviewPage::privateSendReset(){
darkSendPool.ResetPool();
privateSendClient.ResetPool();
QMessageBox::warning(this, tr("PrivateSend"),
tr("PrivateSend was successfully reset."),
@ -595,7 +595,7 @@ void OverviewPage::togglePrivateSend(){
QMessageBox::Ok, QMessageBox::Ok);
settings.setValue("hasMixed", "hasMixed");
}
if(!fEnablePrivateSend){
if(!privateSendClient.fEnablePrivateSend){
CAmount nMinAmount = vecPrivateSendDenominations.back() + PRIVATESEND_COLLATERAL*4;
if(currentBalance < nMinAmount){
QString strMinAmount(BitcoinUnits::formatWithUnit(nDisplayUnit, nMinAmount));
@ -612,7 +612,7 @@ void OverviewPage::togglePrivateSend(){
if(!ctx.isValid())
{
//unlock was cancelled
darkSendPool.nCachedNumBlocks = std::numeric_limits<int>::max();
privateSendClient.nCachedNumBlocks = std::numeric_limits<int>::max();
QMessageBox::warning(this, tr("PrivateSend"),
tr("Wallet is locked and user declined to unlock. Disabling PrivateSend."),
QMessageBox::Ok, QMessageBox::Ok);
@ -623,18 +623,18 @@ void OverviewPage::togglePrivateSend(){
}
fEnablePrivateSend = !fEnablePrivateSend;
darkSendPool.nCachedNumBlocks = std::numeric_limits<int>::max();
privateSendClient.fEnablePrivateSend = !privateSendClient.fEnablePrivateSend;
privateSendClient.nCachedNumBlocks = std::numeric_limits<int>::max();
if(!fEnablePrivateSend){
if(!privateSendClient.fEnablePrivateSend){
ui->togglePrivateSend->setText(tr("Start Mixing"));
darkSendPool.UnlockCoins();
privateSendClient.UnlockCoins();
} else {
ui->togglePrivateSend->setText(tr("Stop Mixing"));
/* show darksend configuration if client has defaults set */
if(nPrivateSendAmount == 0){
if(privateSendClient.nPrivateSendAmount == 0){
DarksendConfig dlg(this);
dlg.setModel(walletModel);
dlg.exec();
@ -669,5 +669,5 @@ void OverviewPage::DisablePrivateSendCompletely() {
if (nWalletBackups <= 0) {
ui->labelPrivateSendEnabled->setText("<span style='color:red;'>(" + tr("Disabled") + ")</span>");
}
fEnablePrivateSend = false;
privateSendClient.fEnablePrivateSend = false;
}

View File

@ -23,7 +23,7 @@
#include "txmempool.h"
#include "wallet/wallet.h"
#include "darksend.h"
#include "privatesend-client.h"
#include <QMessageBox>
#include <QScrollBar>

View File

@ -11,8 +11,8 @@
#include "timedata.h"
#include "wallet/wallet.h"
#include "darksend.h"
#include "instantx.h"
#include "privatesend.h"
#include <stdint.h>

View File

@ -20,9 +20,9 @@
#include "wallet/wallet.h"
#include "wallet/walletdb.h" // for BackupWallet
#include "darksend.h"
#include "instantx.h"
#include "spork.h"
#include "privatesend-client.h"
#include <stdint.h>
@ -141,13 +141,13 @@ void WalletModel::pollBalanceChanged()
if(!lockWallet)
return;
if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks || nPrivateSendRounds != cachedPrivateSendRounds || cachedTxLocks != nCompleteTXLocks)
if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks || privateSendClient.nPrivateSendRounds != cachedPrivateSendRounds || cachedTxLocks != nCompleteTXLocks)
{
fForceCheckBalanceChanged = false;
// Balance and number of transactions might have changed
cachedNumBlocks = chainActive.Height();
cachedPrivateSendRounds = nPrivateSendRounds;
cachedPrivateSendRounds = privateSendClient.nPrivateSendRounds;
checkBalanceChanged();
if(transactionTableModel)

View File

@ -3,13 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "activemasternode.h"
#include "darksend.h"
#include "init.h"
#include "main.h"
#include "masternode-payments.h"
#include "masternode-sync.h"
#include "masternodeconfig.h"
#include "masternodeman.h"
#include "privatesend-client.h"
#include "privatesend-server.h"
#include "rpcserver.h"
#include "util.h"
#include "utilmoneystr.h"
@ -42,18 +43,18 @@ UniValue privatesend(const UniValue& params, bool fHelp)
if(fMasterNode)
return "Mixing is not supported from masternodes";
fEnablePrivateSend = true;
bool result = darkSendPool.DoAutomaticDenominating();
return "Mixing " + (result ? "started successfully" : ("start failed: " + darkSendPool.GetStatus() + ", will retry"));
privateSendClient.fEnablePrivateSend = true;
bool result = privateSendClient.DoAutomaticDenominating();
return "Mixing " + (result ? "started successfully" : ("start failed: " + privateSendClient.GetStatus() + ", will retry"));
}
if(params[0].get_str() == "stop") {
fEnablePrivateSend = false;
privateSendClient.fEnablePrivateSend = false;
return "Mixing was stopped";
}
if(params[0].get_str() == "reset") {
darkSendPool.ResetPool();
privateSendClient.ResetPool();
return "Mixing was reset";
}
@ -67,16 +68,18 @@ UniValue getpoolinfo(const UniValue& params, bool fHelp)
"getpoolinfo\n"
"Returns an object containing mixing pool related information.\n");
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("state", darkSendPool.GetStateString()));
obj.push_back(Pair("mixing_mode", fPrivateSendMultiSession ? "multi-session" : "normal"));
obj.push_back(Pair("queue", darkSendPool.GetQueueSize()));
obj.push_back(Pair("entries", darkSendPool.GetEntriesCount()));
obj.push_back(Pair("status", darkSendPool.GetStatus()));
CPrivateSend privateSend = fMasterNode ? (CPrivateSend)privateSendServer : (CPrivateSend)privateSendClient;
if (darkSendPool.pSubmittedToMasternode) {
obj.push_back(Pair("outpoint", darkSendPool.pSubmittedToMasternode->vin.prevout.ToStringShort()));
obj.push_back(Pair("addr", darkSendPool.pSubmittedToMasternode->addr.ToString()));
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("state", privateSend.GetStateString()));
obj.push_back(Pair("mixing_mode", (!fMasterNode && privateSendClient.fPrivateSendMultiSession) ? "multi-session" : "normal"));
obj.push_back(Pair("queue", privateSend.GetQueueSize()));
obj.push_back(Pair("entries", privateSend.GetEntriesCount()));
obj.push_back(Pair("status", privateSendClient.GetStatus()));
if (privateSendClient.infoMixingMasternode.fInfoValid) {
obj.push_back(Pair("outpoint", privateSendClient.infoMixingMasternode.vin.prevout.ToStringShort()));
obj.push_back(Pair("addr", privateSendClient.infoMixingMasternode.addr.ToString()));
}
if (pwalletMain) {

View File

@ -26,10 +26,10 @@
#include "util.h"
#include "utilmoneystr.h"
#include "darksend.h"
#include "governance.h"
#include "instantx.h"
#include "keepass.h"
#include "privatesend-client.h"
#include "spork.h"
#include <assert.h>
@ -1127,7 +1127,7 @@ int CWallet::GetInputPrivateSendRounds(CTxIn txin) const
{
LOCK(cs_wallet);
int realPrivateSendRounds = GetRealInputPrivateSendRounds(txin, 0);
return realPrivateSendRounds > nPrivateSendRounds ? nPrivateSendRounds : realPrivateSendRounds;
return realPrivateSendRounds > privateSendClient.nPrivateSendRounds ? privateSendClient.nPrivateSendRounds : realPrivateSendRounds;
}
bool CWallet::IsDenominated(const CTxIn &txin) const
@ -1667,7 +1667,7 @@ CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const
if(pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominated(txin)) continue;
const int nRounds = pwallet->GetInputPrivateSendRounds(txin);
if(nRounds >= nPrivateSendRounds){
if(nRounds >= privateSendClient.nPrivateSendRounds){
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAnonymizedCredit() : value out of range");
@ -1953,7 +1953,7 @@ CAmount CWallet::GetNormalizedAnonymizedBalance() const
if (pcoin->GetDepthInMainChain() < 0) continue;
int nRounds = GetInputPrivateSendRounds(txin);
nTotal += pcoin->vout[i].nValue * nRounds / nPrivateSendRounds;
nTotal += pcoin->vout[i].nValue * nRounds / privateSendClient.nPrivateSendRounds;
}
}
}
@ -1966,7 +1966,7 @@ CAmount CWallet::GetNeedsToBeAnonymizedBalance(CAmount nMinBalance) const
if(fLiteMode) return 0;
CAmount nAnonymizedBalance = GetAnonymizedBalance();
CAmount nNeedsToAnonymizeBalance = nPrivateSendAmount*COIN - nAnonymizedBalance;
CAmount nNeedsToAnonymizeBalance = privateSendClient.nPrivateSendAmount*COIN - nAnonymizedBalance;
// try to overshoot target DS balance up to nMinBalance
nNeedsToAnonymizeBalance += nMinBalance;
@ -1980,7 +1980,7 @@ CAmount CWallet::GetNeedsToBeAnonymizedBalance(CAmount nMinBalance) const
if(nNeedsToAnonymizeBalance > nAnonymizableBalance) nNeedsToAnonymizeBalance = nAnonymizableBalance;
// we should never exceed the pool max
if (nNeedsToAnonymizeBalance > darkSendPool.GetMaxPoolAmount()) nNeedsToAnonymizeBalance = darkSendPool.GetMaxPoolAmount();
if (nNeedsToAnonymizeBalance > privateSendClient.GetMaxPoolAmount()) nNeedsToAnonymizeBalance = privateSendClient.GetMaxPoolAmount();
return nNeedsToAnonymizeBalance;
}
@ -2353,7 +2353,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
CTxIn txin = CTxIn(out.tx->GetHash(),out.i);
int nRounds = GetInputPrivateSendRounds(txin);
// make sure it's actually anonymized
if(nRounds < nPrivateSendRounds) continue;
if(nRounds < privateSendClient.nPrivateSendRounds) continue;
}
nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
@ -2375,7 +2375,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
CTxIn txin = CTxIn(out.tx->GetHash(),out.i);
int nRounds = GetInputPrivateSendRounds(txin);
// make sure it's actually anonymized
if(nRounds < nPrivateSendRounds) continue;
if(nRounds < privateSendClient.nPrivateSendRounds) continue;
nValueRet += nDenom;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
@ -2499,7 +2499,7 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount
// bit 3 - .1DASH+1
std::vector<int> vecBits;
if (!darkSendPool.GetDenominationsBits(nDenom, vecBits)) {
if (!privateSendClient.GetDenominationsBits(nDenom, vecBits)) {
return false;
}
@ -2596,7 +2596,7 @@ bool CWallet::SelectCoinsGrouppedByAddresses(std::vector<CompactTallyItem>& vecT
// otherwise they will just lead to higher fee / lower priority
if(wtx.vout[i].nValue <= vecPrivateSendDenominations.back()/10) continue;
// ignore anonymized
if(GetInputPrivateSendRounds(CTxIn(wtx.GetHash(), i)) >= nPrivateSendRounds) continue;
if(GetInputPrivateSendRounds(CTxIn(wtx.GetHash(), i)) >= privateSendClient.nPrivateSendRounds) continue;
}
CompactTallyItem& item = mapTally[address];
@ -3045,7 +3045,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nFeeRet += nChange;
wtxNew.mapValue["DS"] = "1";
// recheck skipped denominations during next mixing
darkSendPool.ClearSkippedDenominations();
privateSendClient.ClearSkippedDenominations();
} else {
// Fill a vout to ourself
@ -3446,7 +3446,7 @@ bool CWallet::NewKeyPool()
BOOST_FOREACH(int64_t nIndex, setKeyPool)
walletdb.ErasePool(nIndex);
setKeyPool.clear();
fEnablePrivateSend = false;
privateSendClient.fEnablePrivateSend = false;
nKeysLeftSinceAutoBackup = 0;
if (IsLocked(true))