mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
Refactor PS (#1437)
* split CDarksendPool * split DoAutomaticDenominating * CMasternode* -> masternode_info_t * move some globals into CPrivateSendClient * addressed PR comments
This commit is contained in:
parent
411332f94b
commit
7242e29228
@ -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 \
|
||||
|
2473
src/darksend.cpp
2473
src/darksend.cpp
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
35
src/init.cpp
35
src/init.cpp
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
1395
src/privatesend-client.cpp
Normal file
File diff suppressed because it is too large
Load Diff
143
src/privatesend-client.h
Normal file
143
src/privatesend-client.h
Normal 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
861
src/privatesend-server.cpp
Normal 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
82
src/privatesend-server.h
Normal 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
416
src/privatesend.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "txmempool.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
#include "darksend.h"
|
||||
#include "privatesend-client.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QScrollBar>
|
||||
|
@ -11,8 +11,8 @@
|
||||
#include "timedata.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
#include "darksend.h"
|
||||
#include "instantx.h"
|
||||
#include "privatesend.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user