2018-11-05 10:29:33 +01:00
// Copyright (c) 2014-2018 The Dash Core developers
2017-05-05 13:26:27 +02:00
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2018-11-05 10:29:33 +01:00
2017-05-05 13:26:27 +02:00
# 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"
2016-11-25 20:01:56 +01:00
# include "netmessagemaker.h"
2017-12-01 19:53:34 +01:00
# include "script/interpreter.h"
2017-05-05 13:26:27 +02:00
# include "txmempool.h"
# include "util.h"
# include "utilmoneystr.h"
CPrivateSendServer privateSendServer ;
2017-02-06 14:31:37 +01:00
void CPrivateSendServer : : ProcessMessage ( CNode * pfrom , const std : : string & strCommand , CDataStream & vRecv , CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return ;
if ( fLiteMode ) return ; // ignore all Dash related functionality
if ( ! masternodeSync . IsBlockchainSynced ( ) ) return ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( strCommand = = NetMsgType : : DSACCEPT ) {
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2018-03-15 10:21:43 +01:00
LogPrint ( " privatesend " , " DSACCEPT -- peer=%d using obsolete version %i \n " , pfrom - > id , pfrom - > nVersion ) ;
2018-11-05 10:29:07 +01:00
connman . PushMessage ( pfrom , CNetMsgMaker ( pfrom - > GetSendVersion ( ) ) . Make ( NetMsgType : : REJECT , strCommand , REJECT_OBSOLETE , strprintf ( " Version must be %d or greater " , MIN_PRIVATESEND_PEER_PROTO_VERSION ) ) ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_VERSION , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-11-05 10:29:07 +01:00
if ( IsSessionReady ( ) ) {
2017-05-05 13:26:27 +02:00
// too many users in this session already, reject new ones
LogPrintf ( " DSACCEPT -- queue is already full! \n " ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_ACCEPTED , ERR_QUEUE_FULL , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-10-25 16:31:32 +02:00
CPrivateSendAccept dsa ;
2018-01-26 02:11:15 +01:00
vRecv > > dsa ;
2017-05-05 13:26:27 +02:00
2018-01-26 02:11:15 +01:00
LogPrint ( " privatesend " , " DSACCEPT -- nDenom %d (%s) txCollateral %s " , dsa . nDenom , CPrivateSend : : GetDenominationsToString ( dsa . nDenom ) , dsa . txCollateral . ToString ( ) ) ;
2017-05-05 13:26:27 +02:00
2017-09-11 16:13:48 +02:00
masternode_info_t mnInfo ;
2018-11-05 10:29:07 +01:00
if ( ! mnodeman . GetMasternodeInfo ( activeMasternodeInfo . outpoint , mnInfo ) ) {
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_MN_LIST , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-11-25 21:52:56 +01:00
if ( vecSessionCollaterals . size ( ) = = 0 ) {
{
TRY_LOCK ( cs_vecqueue , lockRecv ) ;
if ( ! lockRecv ) return ;
2018-11-25 14:27:43 +01:00
2018-11-25 21:52:56 +01:00
for ( const auto & q : vecPrivateSendQueue ) {
if ( q . masternodeOutpoint = = activeMasternodeInfo . outpoint ) {
// refuse to create another queue this often
LogPrint ( " privatesend " , " DSACCEPT -- last dsq is still in queue, refuse to mix \n " ) ;
PushStatus ( pfrom , STATUS_REJECTED , ERR_RECENT , connman ) ;
return ;
}
2018-11-25 14:27:43 +01:00
}
}
2018-11-25 21:52:56 +01:00
if ( mnInfo . nLastDsq ! = 0 & & mnInfo . nLastDsq + mnodeman . CountMasternodes ( ) / 5 > mnodeman . nDsqCount ) {
LogPrintf ( " DSACCEPT -- last dsq too recent, must wait: addr=%s \n " , pfrom - > addr . ToString ( ) ) ;
PushStatus ( pfrom , STATUS_REJECTED , ERR_RECENT , connman ) ;
return ;
}
2017-05-05 13:26:27 +02:00
}
PoolMessage nMessageID = MSG_NOERR ;
2018-11-05 10:29:07 +01:00
bool fResult = nSessionID = = 0 ? CreateNewSession ( dsa , nMessageID , connman )
: AddUserToExistingSession ( dsa , nMessageID ) ;
if ( fResult ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " DSACCEPT -- is compatible, please submit! \n " ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_ACCEPTED , nMessageID , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
} else {
LogPrintf ( " DSACCEPT -- not compatible with existing transactions! \n " ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , nMessageID , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-11-05 10:29:07 +01:00
} else if ( strCommand = = NetMsgType : : DSQUEUE ) {
2018-09-04 12:54:59 +02:00
TRY_LOCK ( cs_vecqueue , lockRecv ) ;
2018-11-05 10:29:07 +01:00
if ( ! lockRecv ) return ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2018-03-15 10:21:43 +01:00
LogPrint ( " privatesend " , " DSQUEUE -- peer=%d using obsolete version %i \n " , pfrom - > id , pfrom - > nVersion ) ;
2018-11-05 10:29:07 +01:00
connman . PushMessage ( pfrom , CNetMsgMaker ( pfrom - > GetSendVersion ( ) ) . Make ( NetMsgType : : REJECT , strCommand , REJECT_OBSOLETE , strprintf ( " Version must be %d or greater " , MIN_PRIVATESEND_PEER_PROTO_VERSION ) ) ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-10-25 16:31:32 +02:00
CPrivateSendQueue dsq ;
2017-05-05 13:26:27 +02:00
vRecv > > dsq ;
// process every dsq only once
2018-10-25 16:31:32 +02:00
for ( const auto & q : vecPrivateSendQueue ) {
2018-11-05 10:29:07 +01:00
if ( q = = dsq ) {
2017-05-05 13:26:27 +02:00
// LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString());
return ;
}
}
LogPrint ( " privatesend " , " DSQUEUE -- %s new \n " , dsq . ToString ( ) ) ;
2018-11-05 10:29:07 +01:00
if ( dsq . IsExpired ( ) ) return ;
2017-05-05 13:26:27 +02:00
2017-09-11 16:13:48 +02:00
masternode_info_t mnInfo ;
2018-11-05 10:29:07 +01:00
if ( ! mnodeman . GetMasternodeInfo ( dsq . masternodeOutpoint , mnInfo ) ) return ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( ! dsq . CheckSignature ( mnInfo . legacyKeyIDOperator , mnInfo . blsPubKeyOperator ) ) {
2017-05-05 13:26:27 +02:00
// we probably have outdated info
2018-02-15 08:29:44 +01:00
mnodeman . AskForMN ( pfrom , dsq . masternodeOutpoint , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-11-05 10:29:07 +01:00
if ( ! dsq . fReady ) {
2018-10-25 16:31:32 +02:00
for ( const auto & q : vecPrivateSendQueue ) {
2018-11-05 10:29:07 +01:00
if ( q . masternodeOutpoint = = dsq . masternodeOutpoint ) {
2017-05-05 13:26:27 +02:00
// no way same mn can send another "not yet ready" dsq this soon
2017-09-11 16:13:48 +02:00
LogPrint ( " privatesend " , " DSQUEUE -- Masternode %s is sending WAY too many dsq messages \n " , mnInfo . addr . ToString ( ) ) ;
2017-05-05 13:26:27 +02:00
return ;
}
}
2018-11-22 06:09:35 +01:00
int nThreshold = mnInfo . nLastDsq + mnodeman . CountMasternodes ( ) / 5 ;
2017-09-11 16:13:48 +02:00
LogPrint ( " privatesend " , " DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d \n " , mnInfo . nLastDsq , nThreshold , mnodeman . nDsqCount ) ;
2017-05-05 13:26:27 +02:00
//don't allow a few nodes to dominate the queuing process
2018-11-05 10:29:07 +01:00
if ( mnInfo . nLastDsq ! = 0 & & nThreshold > mnodeman . nDsqCount ) {
2017-09-11 16:13:48 +02:00
LogPrint ( " privatesend " , " DSQUEUE -- Masternode %s is sending too many dsq messages \n " , mnInfo . addr . ToString ( ) ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-02-15 08:29:44 +01:00
mnodeman . AllowMixing ( dsq . masternodeOutpoint ) ;
2017-05-05 13:26:27 +02:00
2017-09-11 16:13:48 +02:00
LogPrint ( " privatesend " , " DSQUEUE -- new PrivateSend queue (%s) from masternode %s \n " , dsq . ToString ( ) , mnInfo . addr . ToString ( ) ) ;
2018-10-25 16:31:32 +02:00
vecPrivateSendQueue . push_back ( dsq ) ;
2017-09-19 16:51:38 +02:00
dsq . Relay ( connman ) ;
2017-05-05 13:26:27 +02:00
}
2018-11-05 10:29:07 +01:00
} else if ( strCommand = = NetMsgType : : DSVIN ) {
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2018-03-15 10:21:43 +01:00
LogPrint ( " privatesend " , " DSVIN -- peer=%d using obsolete version %i \n " , pfrom - > id , pfrom - > nVersion ) ;
2018-11-05 10:29:07 +01:00
connman . PushMessage ( pfrom , CNetMsgMaker ( pfrom - > GetSendVersion ( ) ) . Make ( NetMsgType : : REJECT , strCommand , REJECT_OBSOLETE , strprintf ( " Version must be %d or greater " , MIN_PRIVATESEND_PEER_PROTO_VERSION ) ) ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_VERSION , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
//do we have enough users in the current session?
2018-11-05 10:29:07 +01:00
if ( ! IsSessionReady ( ) ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " DSVIN -- session not complete! \n " ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_SESSION , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-10-25 16:31:32 +02:00
CPrivateSendEntry entry ;
2017-05-05 13:26:27 +02:00
vRecv > > entry ;
2017-09-20 14:02:53 +02:00
LogPrint ( " privatesend " , " DSVIN -- txCollateral %s " , entry . txCollateral - > ToString ( ) ) ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( entry . vecTxDSIn . size ( ) > PRIVATESEND_ENTRY_MAX_SIZE ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " DSVIN -- ERROR: too many inputs! %d/%d \n " , entry . vecTxDSIn . size ( ) , PRIVATESEND_ENTRY_MAX_SIZE ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_MAXIMUM , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-11-05 10:29:07 +01:00
if ( entry . vecTxOut . size ( ) > PRIVATESEND_ENTRY_MAX_SIZE ) {
2017-12-04 07:06:07 +01:00
LogPrintf ( " DSVIN -- ERROR: too many outputs! %d/%d \n " , entry . vecTxOut . size ( ) , PRIVATESEND_ENTRY_MAX_SIZE ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_MAXIMUM , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
//do we have the same denominations as the current session?
2018-11-05 10:29:07 +01:00
if ( ! IsOutputsCompatibleWithSessionDenom ( entry . vecTxOut ) ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " DSVIN -- not compatible with existing transactions! \n " ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_EXISTING_TX , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
//check it like a transaction
{
CAmount nValueIn = 0 ;
CAmount nValueOut = 0 ;
CMutableTransaction tx ;
2017-12-04 07:06:07 +01:00
for ( const auto & txout : entry . vecTxOut ) {
2017-05-05 13:26:27 +02:00
nValueOut + = txout . nValue ;
tx . vout . push_back ( txout ) ;
2018-11-05 10:29:07 +01:00
if ( txout . scriptPubKey . size ( ) ! = 25 ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " DSVIN -- non-standard pubkey detected! scriptPubKey=%s \n " , ScriptToAsmStr ( txout . scriptPubKey ) ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_NON_STANDARD_PUBKEY , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
2018-11-05 10:29:07 +01:00
if ( ! txout . scriptPubKey . IsPayToPublicKeyHash ( ) ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " DSVIN -- invalid script! scriptPubKey=%s \n " , ScriptToAsmStr ( txout . scriptPubKey ) ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_INVALID_SCRIPT , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
}
2018-02-06 12:09:33 +01:00
for ( const auto & txin : entry . vecTxDSIn ) {
2017-05-05 13:26:27 +02:00
tx . vin . push_back ( txin ) ;
LogPrint ( " privatesend " , " DSVIN -- txin=%s \n " , txin . ToString ( ) ) ;
2017-09-26 16:33:46 +02:00
Coin coin ;
2018-11-05 10:29:07 +01:00
if ( GetUTXOCoin ( txin . prevout , coin ) ) {
2017-09-26 16:33:46 +02:00
nValueIn + = coin . out . nValue ;
2017-05-05 13:26:27 +02:00
} else {
2018-02-26 12:10:20 +01:00
LogPrintf ( " DSVIN -- missing input! txin=%s \n " , txin . ToString ( ) ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_MISSING_TX , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
}
// There should be no fee in mixing tx
CAmount nFee = nValueIn - nValueOut ;
2018-11-05 10:29:07 +01:00
if ( nFee ! = 0 ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " DSVIN -- there should be no fee in mixing tx! fees: %lld, tx=%s " , nFee , tx . ToString ( ) ) ;
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_FEES , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
}
PoolMessage nMessageID = MSG_NOERR ;
2017-07-25 12:57:26 +02:00
entry . addr = pfrom - > addr ;
2018-11-05 10:29:07 +01:00
if ( AddEntry ( entry , nMessageID ) ) {
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_ACCEPTED , nMessageID , connman ) ;
CheckPool ( connman ) ;
RelayStatus ( STATUS_ACCEPTED , connman ) ;
2017-05-05 13:26:27 +02:00
} else {
2017-09-19 16:51:38 +02:00
PushStatus ( pfrom , STATUS_REJECTED , nMessageID , connman ) ;
2017-05-05 13:26:27 +02:00
SetNull ( ) ;
}
2018-11-05 10:29:07 +01:00
} else if ( strCommand = = NetMsgType : : DSSIGNFINALTX ) {
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2018-03-15 10:21:43 +01:00
LogPrint ( " privatesend " , " DSSIGNFINALTX -- peer=%d using obsolete version %i \n " , pfrom - > id , pfrom - > nVersion ) ;
2018-11-05 10:29:07 +01:00
connman . PushMessage ( pfrom , CNetMsgMaker ( pfrom - > GetSendVersion ( ) ) . Make ( NetMsgType : : REJECT , strCommand , REJECT_OBSOLETE , strprintf ( " Version must be %d or greater " , MIN_PRIVATESEND_PEER_PROTO_VERSION ) ) ) ;
2017-05-05 13:26:27 +02:00
return ;
}
std : : vector < CTxIn > vecTxIn ;
vRecv > > vecTxIn ;
LogPrint ( " privatesend " , " DSSIGNFINALTX -- vecTxIn.size() %s \n " , vecTxIn . size ( ) ) ;
int nTxInIndex = 0 ;
int nTxInsCount = ( int ) vecTxIn . size ( ) ;
2018-02-06 12:09:33 +01:00
for ( const auto & txin : vecTxIn ) {
2017-05-05 13:26:27 +02:00
nTxInIndex + + ;
2018-11-05 10:29:07 +01:00
if ( ! AddScriptSig ( txin ) ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d \n " , nTxInIndex , nTxInsCount , nSessionID ) ;
2017-09-19 16:51:38 +02:00
RelayStatus ( STATUS_REJECTED , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
LogPrint ( " privatesend " , " DSSIGNFINALTX -- AddScriptSig() %d/%d success \n " , nTxInIndex , nTxInsCount ) ;
}
// all is good
2017-09-19 16:51:38 +02:00
CheckPool ( connman ) ;
2017-05-05 13:26:27 +02:00
}
}
void CPrivateSendServer : : SetNull ( )
{
// MN side
vecSessionCollaterals . clear ( ) ;
2018-09-04 12:54:59 +02:00
CPrivateSendBaseSession : : SetNull ( ) ;
CPrivateSendBaseManager : : SetNull ( ) ;
2017-05-05 13:26:27 +02:00
}
//
// Check the mixing progress and send client updates if a Masternode
//
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : CheckPool ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-03-21 12:10:01 +01:00
if ( ! fMasternodeMode ) return ;
2017-05-05 13:26:27 +02:00
2018-03-21 12:10:01 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::CheckPool -- entries count %lu \n " , GetEntriesCount ( ) ) ;
2017-05-05 13:26:27 +02:00
2018-03-21 12:10:01 +01:00
// If entries are full, create finalized transaction
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES & & GetEntriesCount ( ) > = CPrivateSend : : GetMaxPoolTransactions ( ) ) {
LogPrint ( " privatesend " , " CPrivateSendServer::CheckPool -- FINALIZE TRANSACTIONS \n " ) ;
CreateFinalTransaction ( connman ) ;
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 ( connman ) ;
return ;
2017-05-05 13:26:27 +02:00
}
}
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : CreateFinalTransaction ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
LogPrint ( " privatesend " , " CPrivateSendServer::CreateFinalTransaction -- FINALIZE TRANSACTIONS \n " ) ;
CMutableTransaction txNew ;
// make our new transaction
2018-11-05 10:29:07 +01:00
for ( int i = 0 ; i < GetEntriesCount ( ) ; i + + ) {
2017-12-04 07:06:07 +01:00
for ( const auto & txout : vecEntries [ i ] . vecTxOut )
txNew . vout . push_back ( txout ) ;
2017-05-05 13:26:27 +02:00
2018-02-06 12:09:33 +01:00
for ( const auto & txdsin : vecEntries [ i ] . vecTxDSIn )
2017-05-05 13:26:27 +02:00
txNew . vin . push_back ( txdsin ) ;
}
2017-07-10 16:42:59 +02:00
sort ( txNew . vin . begin ( ) , txNew . vin . end ( ) , CompareInputBIP69 ( ) ) ;
sort ( txNew . vout . begin ( ) , txNew . vout . end ( ) , CompareOutputBIP69 ( ) ) ;
2017-05-05 13:26:27 +02:00
finalMutableTransaction = txNew ;
LogPrint ( " privatesend " , " CPrivateSendServer::CreateFinalTransaction -- finalMutableTransaction=%s " , txNew . ToString ( ) ) ;
// request signatures from clients
2017-09-19 16:51:38 +02:00
RelayFinalTransaction ( finalMutableTransaction , connman ) ;
2017-05-05 13:26:27 +02:00
SetState ( POOL_STATE_SIGNING ) ;
}
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : CommitFinalTransaction ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return ; // check and relay final tx only on masternode
2017-05-05 13:26:27 +02:00
2017-01-04 12:22:49 +01:00
CTransactionRef finalTransaction = MakeTransactionRef ( finalMutableTransaction ) ;
uint256 hashTx = finalTransaction - > GetHash ( ) ;
2017-05-05 13:26:27 +02:00
2017-01-04 12:22:49 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::CommitFinalTransaction -- finalTransaction=%s " , finalTransaction - > ToString ( ) ) ;
2017-05-05 13:26:27 +02:00
{
// See if the transaction is valid
TRY_LOCK ( cs_main , lockMain ) ;
CValidationState validationState ;
2018-11-05 10:29:07 +01:00
mempool . PrioritiseTransaction ( hashTx , hashTx . ToString ( ) , 1000 , 0.1 * COIN ) ;
if ( ! lockMain | | ! AcceptToMemoryPool ( mempool , validationState , finalTransaction , false , NULL , false , maxTxFee , true ) ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " CPrivateSendServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid \n " ) ;
SetNull ( ) ;
// not much we can do in this case, just notify clients
2017-09-19 16:51:38 +02:00
RelayCompletedTransaction ( ERR_INVALID_TX , connman ) ;
2017-05-05 13:26:27 +02:00
return ;
}
}
LogPrintf ( " CPrivateSendServer::CommitFinalTransaction -- CREATING DSTX \n " ) ;
// create and sign masternode dstx transaction
2018-11-05 10:29:07 +01:00
if ( ! CPrivateSend : : GetDSTX ( hashTx ) ) {
2018-10-25 16:31:32 +02:00
CPrivateSendBroadcastTx dstxNew ( finalTransaction , activeMasternodeInfo . outpoint , GetAdjustedTime ( ) ) ;
2017-06-30 20:30:16 +02:00
dstxNew . Sign ( ) ;
CPrivateSend : : AddDSTX ( dstxNew ) ;
2017-05-05 13:26:27 +02:00
}
LogPrintf ( " CPrivateSendServer::CommitFinalTransaction -- TRANSMITTING DSTX \n " ) ;
CInv inv ( MSG_DSTX , hashTx ) ;
2017-09-19 16:51:38 +02:00
connman . RelayInv ( inv ) ;
2017-05-05 13:26:27 +02:00
// Tell the clients it was successful
2017-09-19 16:51:38 +02:00
RelayCompletedTransaction ( MSG_SUCCESS , connman ) ;
2017-05-05 13:26:27 +02:00
// Randomly charge clients
2017-09-19 16:51:38 +02:00
ChargeRandomFees ( connman ) ;
2017-05-05 13:26:27 +02:00
// 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
2018-02-08 06:46:44 +01:00
// would be able to do this over and over again and bring the mixing to a halt.
2017-05-05 13:26:27 +02:00
//
// 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.
//
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : ChargeFees ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return ;
2017-05-05 13:26:27 +02:00
//we don't need to charge collateral for every offence.
2018-11-05 10:29:07 +01:00
if ( GetRandInt ( 100 ) > 33 ) return ;
2017-05-05 13:26:27 +02:00
2017-09-20 14:02:53 +02:00
std : : vector < CTransactionRef > vecOffendersCollaterals ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES ) {
2018-02-06 12:09:33 +01:00
for ( const auto & txCollateral : vecSessionCollaterals ) {
2017-05-05 13:26:27 +02:00
bool fFound = false ;
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries )
2018-11-05 10:29:07 +01:00
if ( * entry . txCollateral = = * txCollateral )
2017-05-05 13:26:27 +02:00
fFound = true ;
// This queue entry didn't send us the promised transaction
2018-11-05 10:29:07 +01:00
if ( ! fFound ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " CPrivateSendServer::ChargeFees -- found uncooperative node (didn't send transaction), found offence \n " ) ;
vecOffendersCollaterals . push_back ( txCollateral ) ;
}
}
}
2018-11-05 10:29:07 +01:00
if ( nState = = POOL_STATE_SIGNING ) {
2017-05-05 13:26:27 +02:00
// who didn't sign?
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries ) {
for ( const auto & txdsin : entry . vecTxDSIn ) {
2018-11-05 10:29:07 +01:00
if ( ! txdsin . fHasSig ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " CPrivateSendServer::ChargeFees -- found uncooperative node (didn't sign), found offence \n " ) ;
vecOffendersCollaterals . push_back ( entry . txCollateral ) ;
}
}
}
}
// no offences found
2018-11-05 10:29:07 +01:00
if ( vecOffendersCollaterals . empty ( ) ) return ;
2017-05-05 13:26:27 +02:00
//mostly offending? Charge sometimes
2018-11-05 10:29:07 +01:00
if ( ( int ) vecOffendersCollaterals . size ( ) > = Params ( ) . PoolMaxTransactions ( ) - 1 & & GetRandInt ( 100 ) > 33 ) return ;
2017-05-05 13:26:27 +02:00
//everyone is an offender? That's not right
2018-11-05 10:29:07 +01:00
if ( ( int ) vecOffendersCollaterals . size ( ) > = Params ( ) . PoolMaxTransactions ( ) ) return ;
2017-05-05 13:26:27 +02:00
//charge one of the offenders randomly
std : : random_shuffle ( vecOffendersCollaterals . begin ( ) , vecOffendersCollaterals . end ( ) ) ;
2018-11-05 10:29:07 +01:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES | | nState = = POOL_STATE_SIGNING ) {
2018-09-28 09:55:11 +02:00
LogPrintf ( " CPrivateSendServer::ChargeFees -- found uncooperative node (didn't %s transaction), charging fees: %s " ,
2018-11-05 10:29:07 +01:00
( nState = = POOL_STATE_SIGNING ) ? " sign " : " send " , vecOffendersCollaterals [ 0 ] - > ToString ( ) ) ;
2017-05-05 13:26:27 +02:00
2018-06-11 12:14:41 +02:00
LOCK ( cs_main ) ;
CValidationState state ;
2018-11-05 10:29:07 +01:00
if ( ! AcceptToMemoryPool ( mempool , state , vecOffendersCollaterals [ 0 ] , false , NULL , false , maxTxFee ) ) {
2018-06-11 12:14:41 +02:00
// should never really happen
LogPrintf ( " CPrivateSendServer::ChargeFees -- ERROR: AcceptToMemoryPool failed! \n " ) ;
} else {
connman . RelayTransaction ( * vecOffendersCollaterals [ 0 ] ) ;
}
2017-05-05 13:26:27 +02:00
}
}
/*
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.001 DRK per transaction on average .
*/
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : ChargeRandomFees ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return ;
2017-05-05 13:26:27 +02:00
2018-06-11 12:14:41 +02:00
LOCK ( cs_main ) ;
2018-02-06 12:09:33 +01:00
for ( const auto & txCollateral : vecSessionCollaterals ) {
2018-11-05 10:29:07 +01:00
if ( GetRandInt ( 100 ) > 10 ) return ;
2017-09-20 14:02:53 +02:00
LogPrintf ( " CPrivateSendServer::ChargeRandomFees -- charging random fees, txCollateral=%s " , txCollateral - > ToString ( ) ) ;
2018-06-11 12:14:41 +02:00
CValidationState state ;
2018-11-05 10:29:07 +01:00
if ( ! AcceptToMemoryPool ( mempool , state , txCollateral , false , NULL , false , maxTxFee ) ) {
2018-06-11 12:14:41 +02:00
// should never really happen
LogPrintf ( " CPrivateSendServer::ChargeRandomFees -- ERROR: AcceptToMemoryPool failed! \n " ) ;
} else {
connman . RelayTransaction ( * txCollateral ) ;
}
2017-05-05 13:26:27 +02:00
}
}
//
// Check for various timeouts (queue objects, mixing, etc)
//
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : CheckTimeout ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return ;
2017-05-05 13:26:27 +02:00
2018-03-21 12:10:01 +01:00
CheckQueue ( ) ;
2017-05-05 13:26:27 +02:00
int nTimeout = ( nState = = POOL_STATE_SIGNING ) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT ;
2018-02-12 13:47:53 +01:00
bool fTimeout = GetTime ( ) - nTimeLastSuccessfulStep > = nTimeout ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( nState ! = POOL_STATE_IDLE & & fTimeout ) {
2018-02-08 06:46:44 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::CheckTimeout -- %s timed out (%ds) -- resetting \n " ,
2018-11-05 10:29:07 +01:00
( nState = = POOL_STATE_SIGNING ) ? " Signing " : " Session " , nTimeout ) ;
2017-09-19 16:51:38 +02:00
ChargeFees ( connman ) ;
2017-05-05 13:26:27 +02:00
SetNull ( ) ;
}
}
/*
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
*/
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : CheckForCompleteQueue ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( nState = = POOL_STATE_QUEUE & & IsSessionReady ( ) ) {
2017-05-05 13:26:27 +02:00
SetState ( POOL_STATE_ACCEPTING_ENTRIES ) ;
2018-10-25 16:31:32 +02:00
CPrivateSendQueue dsq ( nSessionDenom , activeMasternodeInfo . outpoint , GetAdjustedTime ( ) , true ) ;
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::CheckForCompleteQueue -- queue is ready, signing and relaying (%s) \n " , dsq . ToString ( ) ) ;
dsq . Sign ( ) ;
2017-09-19 16:51:38 +02:00
dsq . Relay ( connman ) ;
2017-05-05 13:26:27 +02:00
}
}
// 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 ( ) ;
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries ) {
2017-12-04 07:06:07 +01:00
for ( const auto & txout : entry . vecTxOut )
txNew . vout . push_back ( txout ) ;
2017-05-05 13:26:27 +02:00
2018-02-06 12:09:33 +01:00
for ( const auto & txdsin : entry . vecTxDSIn ) {
2017-05-05 13:26:27 +02:00
txNew . vin . push_back ( txdsin ) ;
2018-11-05 10:29:07 +01:00
if ( txdsin . prevout = = txin . prevout ) {
2017-05-05 13:26:27 +02:00
nTxInIndex = i ;
sigPubKey = txdsin . prevPubKey ;
}
i + + ;
}
}
2018-11-05 10:29:07 +01:00
if ( nTxInIndex > = 0 ) { //might have to do this one input at a time?
2017-05-05 13:26:27 +02:00
txNew . vin [ nTxInIndex ] . scriptSig = txin . scriptSig ;
2018-11-05 10:29:07 +01:00
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 ) ) ) {
2017-05-05 13:26:27 +02:00
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
//
2018-10-25 16:31:32 +02:00
bool CPrivateSendServer : : AddEntry ( const CPrivateSendEntry & entryNew , PoolMessage & nMessageIDRet )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return false ;
2017-05-05 13:26:27 +02:00
2018-02-06 12:09:33 +01:00
for ( const auto & txin : entryNew . vecTxDSIn ) {
2018-11-05 10:29:07 +01:00
if ( txin . prevout . IsNull ( ) ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddEntry -- input not valid! \n " ) ;
nMessageIDRet = ERR_INVALID_INPUT ;
return false ;
}
}
2018-11-05 10:29:07 +01:00
if ( ! CPrivateSend : : IsCollateralValid ( * entryNew . txCollateral ) ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddEntry -- collateral not valid! \n " ) ;
nMessageIDRet = ERR_INVALID_COLLATERAL ;
return false ;
}
2018-11-05 10:29:07 +01:00
if ( GetEntriesCount ( ) > = CPrivateSend : : GetMaxPoolTransactions ( ) ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddEntry -- entries is full! \n " ) ;
nMessageIDRet = ERR_ENTRIES_FULL ;
return false ;
}
2018-02-06 12:09:33 +01:00
for ( const auto & txin : entryNew . vecTxDSIn ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " looking for txin -- %s \n " , txin . ToString ( ) ) ;
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries ) {
for ( const auto & txdsin : entry . vecTxDSIn ) {
2018-11-05 10:29:07 +01:00
if ( txdsin . prevout = = txin . prevout ) {
2017-05-05 13:26:27 +02:00
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 ;
2018-02-12 13:47:53 +01:00
nTimeLastSuccessfulStep = GetTime ( ) ;
2017-05-05 13:26:27 +02:00
return true ;
}
bool CPrivateSendServer : : AddScriptSig ( const CTxIn & txinNew )
{
2018-11-05 10:29:07 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddScriptSig -- scriptSig=%s \n " , ScriptToAsmStr ( txinNew . scriptSig ) . substr ( 0 , 24 ) ) ;
2017-05-05 13:26:27 +02:00
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries ) {
for ( const auto & txdsin : entry . vecTxDSIn ) {
2018-11-05 10:29:07 +01:00
if ( txdsin . scriptSig = = txinNew . scriptSig ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddScriptSig -- already exists \n " ) ;
return false ;
}
}
}
2018-11-05 10:29:07 +01:00
if ( ! IsInputScriptSigValid ( txinNew ) ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddScriptSig -- Invalid scriptSig \n " ) ;
return false ;
}
2018-11-05 10:29:07 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddScriptSig -- scriptSig=%s new \n " , ScriptToAsmStr ( txinNew . scriptSig ) . substr ( 0 , 24 ) ) ;
2017-05-05 13:26:27 +02:00
2018-02-06 12:09:33 +01:00
for ( auto & txin : finalMutableTransaction . vin ) {
2018-11-05 10:29:07 +01:00
if ( txin . prevout = = txinNew . prevout & & txin . nSequence = = txinNew . nSequence ) {
2017-05-05 13:26:27 +02:00
txin . scriptSig = txinNew . scriptSig ;
2018-11-05 10:29:07 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s \n " , ScriptToAsmStr ( txinNew . scriptSig ) . substr ( 0 , 24 ) ) ;
2017-05-05 13:26:27 +02:00
}
}
2018-11-05 10:29:07 +01:00
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 ) ) ;
2017-05-05 13:26:27 +02:00
return true ;
}
}
2018-11-05 10:29:07 +01:00
LogPrintf ( " CPrivateSendServer::AddScriptSig -- Couldn't set sig! \n " ) ;
2017-05-05 13:26:27 +02:00
return false ;
}
// Check to make sure everything is signed
bool CPrivateSendServer : : IsSignaturesComplete ( )
{
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries )
for ( const auto & txdsin : entry . vecTxDSIn )
2018-11-05 10:29:07 +01:00
if ( ! txdsin . fHasSig ) return false ;
2017-05-05 13:26:27 +02:00
return true ;
}
2017-12-04 07:06:07 +01:00
bool CPrivateSendServer : : IsOutputsCompatibleWithSessionDenom ( const std : : vector < CTxOut > & vecTxOut )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( CPrivateSend : : GetDenominations ( vecTxOut ) = = 0 ) return false ;
2017-05-05 13:26:27 +02:00
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries ) {
2017-12-04 07:06:07 +01:00
LogPrintf ( " CPrivateSendServer::IsOutputsCompatibleWithSessionDenom -- vecTxOut denom %d, entry.vecTxOut denom %d \n " ,
2018-11-05 10:29:07 +01:00
CPrivateSend : : GetDenominations ( vecTxOut ) , CPrivateSend : : GetDenominations ( entry . vecTxOut ) ) ;
if ( CPrivateSend : : GetDenominations ( vecTxOut ) ! = CPrivateSend : : GetDenominations ( entry . vecTxOut ) ) return false ;
2017-05-05 13:26:27 +02:00
}
return true ;
}
2018-10-25 16:31:32 +02:00
bool CPrivateSendServer : : IsAcceptableDSA ( const CPrivateSendAccept & dsa , PoolMessage & nMessageIDRet )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return false ;
2017-05-05 13:26:27 +02:00
// is denom even smth legit?
std : : vector < int > vecBits ;
2018-11-05 10:29:07 +01:00
if ( ! CPrivateSend : : GetDenominationsBits ( dsa . nDenom , vecBits ) ) {
2018-01-26 02:11:15 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::%s -- denom not valid! \n " , __func__ ) ;
2017-05-05 13:26:27 +02:00
nMessageIDRet = ERR_DENOM ;
return false ;
}
// check collateral
2018-11-05 10:29:07 +01:00
if ( ! fUnitTest & & ! CPrivateSend : : IsCollateralValid ( dsa . txCollateral ) ) {
2018-01-26 02:11:15 +01:00
LogPrint ( " privatesend " , " CPrivateSendServer::%s -- collateral not valid! \n " , __func__ ) ;
2017-05-05 13:26:27 +02:00
nMessageIDRet = ERR_INVALID_COLLATERAL ;
return false ;
}
return true ;
}
2018-10-25 16:31:32 +02:00
bool CPrivateSendServer : : CreateNewSession ( const CPrivateSendAccept & dsa , PoolMessage & nMessageIDRet , CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode | | nSessionID ! = 0 ) return false ;
2017-05-05 13:26:27 +02:00
// new session can only be started in idle mode
2018-11-05 10:29:07 +01:00
if ( nState ! = POOL_STATE_IDLE ) {
2017-05-05 13:26:27 +02:00
nMessageIDRet = ERR_MODE ;
LogPrintf ( " CPrivateSendServer::CreateNewSession -- incompatible mode: nState=%d \n " , nState ) ;
return false ;
}
2018-11-05 10:29:07 +01:00
if ( ! IsAcceptableDSA ( dsa , nMessageIDRet ) ) {
2017-05-05 13:26:27 +02:00
return false ;
}
// start new session
nMessageIDRet = MSG_NOERR ;
2018-11-05 10:29:07 +01:00
nSessionID = GetRandInt ( 999999 ) + 1 ;
2018-01-26 02:11:15 +01:00
nSessionDenom = dsa . nDenom ;
2017-05-05 13:26:27 +02:00
SetState ( POOL_STATE_QUEUE ) ;
2018-02-12 13:47:53 +01:00
nTimeLastSuccessfulStep = GetTime ( ) ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( ! fUnitTest ) {
2017-05-05 13:26:27 +02:00
//broadcast that I'm accepting entries, only if it's the first entry through
2018-10-25 16:31:32 +02:00
CPrivateSendQueue dsq ( nSessionDenom , activeMasternodeInfo . outpoint , GetAdjustedTime ( ) , false ) ;
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::CreateNewSession -- signing and relaying new queue: %s \n " , dsq . ToString ( ) ) ;
dsq . Sign ( ) ;
2017-09-19 16:51:38 +02:00
dsq . Relay ( connman ) ;
2018-10-25 16:31:32 +02:00
vecPrivateSendQueue . push_back ( dsq ) ;
2017-05-05 13:26:27 +02:00
}
2018-01-26 02:11:15 +01:00
vecSessionCollaterals . push_back ( MakeTransactionRef ( dsa . txCollateral ) ) ;
2017-05-05 13:26:27 +02:00
LogPrintf ( " CPrivateSendServer::CreateNewSession -- new session created, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d \n " ,
2018-11-05 10:29:07 +01:00
nSessionID , nSessionDenom , CPrivateSend : : GetDenominationsToString ( nSessionDenom ) , vecSessionCollaterals . size ( ) ) ;
2017-05-05 13:26:27 +02:00
return true ;
}
2018-10-25 16:31:32 +02:00
bool CPrivateSendServer : : AddUserToExistingSession ( const CPrivateSendAccept & dsa , PoolMessage & nMessageIDRet )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode | | nSessionID = = 0 | | IsSessionReady ( ) ) return false ;
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( ! IsAcceptableDSA ( dsa , nMessageIDRet ) ) {
2017-05-05 13:26:27 +02:00
return false ;
}
// we only add new users to an existing session when we are in queue mode
2018-11-05 10:29:07 +01:00
if ( nState ! = POOL_STATE_QUEUE ) {
2017-05-05 13:26:27 +02:00
nMessageIDRet = ERR_MODE ;
LogPrintf ( " CPrivateSendServer::AddUserToExistingSession -- incompatible mode: nState=%d \n " , nState ) ;
return false ;
}
2018-11-05 10:29:07 +01:00
if ( dsa . nDenom ! = nSessionDenom ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " CPrivateSendServer::AddUserToExistingSession -- incompatible denom %d (%s) != nSessionDenom %d (%s) \n " ,
2018-11-05 10:29:07 +01:00
dsa . nDenom , CPrivateSend : : GetDenominationsToString ( dsa . nDenom ) , nSessionDenom , CPrivateSend : : GetDenominationsToString ( nSessionDenom ) ) ;
2017-05-05 13:26:27 +02:00
nMessageIDRet = ERR_DENOM ;
return false ;
}
// count new user as accepted to an existing session
nMessageIDRet = MSG_NOERR ;
2018-02-12 13:47:53 +01:00
nTimeLastSuccessfulStep = GetTime ( ) ;
2018-01-26 02:11:15 +01:00
vecSessionCollaterals . push_back ( MakeTransactionRef ( dsa . txCollateral ) ) ;
2017-05-05 13:26:27 +02:00
2018-09-05 13:19:03 +02:00
LogPrintf ( " CPrivateSendServer::AddUserToExistingSession -- new user accepted, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d \n " ,
2018-11-05 10:29:07 +01:00
nSessionID , nSessionDenom , CPrivateSend : : GetDenominationsToString ( nSessionDenom ) , vecSessionCollaterals . size ( ) ) ;
2017-05-05 13:26:27 +02:00
return true ;
}
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : RelayFinalTransaction ( const CTransaction & txFinal , CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2017-07-25 12:57:26 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::%s -- nSessionID: %d nSessionDenom: %d (%s) \n " ,
2018-11-05 10:29:07 +01:00
__func__ , nSessionID , nSessionDenom , CPrivateSend : : GetDenominationsToString ( nSessionDenom ) ) ;
2017-07-25 12:57:26 +02:00
// final mixing tx with empty signatures should be relayed to mixing participants only
for ( const auto entry : vecEntries ) {
2017-09-19 16:51:38 +02:00
bool fOk = connman . ForNode ( entry . addr , [ & txFinal , & connman , this ] ( CNode * pnode ) {
2016-11-25 20:01:56 +01:00
CNetMsgMaker msgMaker ( pnode - > GetSendVersion ( ) ) ;
connman . PushMessage ( pnode , msgMaker . Make ( NetMsgType : : DSFINALTX , nSessionID , txFinal ) ) ;
2017-07-25 12:57:26 +02:00
return true ;
} ) ;
2018-11-05 10:29:07 +01:00
if ( ! fOk ) {
2017-07-25 12:57:26 +02:00
// no such node? maybe this client disconnected or our own connection went down
2017-09-19 16:51:38 +02:00
RelayStatus ( STATUS_REJECTED , connman ) ;
2017-07-25 12:57:26 +02:00
break ;
}
}
2017-05-05 13:26:27 +02:00
}
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : PushStatus ( CNode * pnode , PoolStatusUpdate nStatusUpdate , PoolMessage nMessageID , CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( ! pnode ) return ;
2016-11-25 20:01:56 +01:00
CNetMsgMaker msgMaker ( pnode - > GetSendVersion ( ) ) ;
connman . PushMessage ( pnode , msgMaker . Make ( NetMsgType : : DSSTATUSUPDATE , nSessionID , ( int ) nState , ( int ) vecEntries . size ( ) , ( int ) nStatusUpdate , ( int ) nMessageID ) ) ;
2017-05-05 13:26:27 +02:00
}
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : RelayStatus ( PoolStatusUpdate nStatusUpdate , CConnman & connman , PoolMessage nMessageID )
2017-05-05 13:26:27 +02:00
{
2017-07-25 12:57:26 +02:00
unsigned int nDisconnected { } ;
// status updates should be relayed to mixing participants only
for ( const auto entry : vecEntries ) {
// make sure everyone is still connected
2017-09-19 16:51:38 +02:00
bool fOk = connman . ForNode ( entry . addr , [ & nStatusUpdate , & nMessageID , & connman , this ] ( CNode * pnode ) {
PushStatus ( pnode , nStatusUpdate , nMessageID , connman ) ;
2017-07-25 12:57:26 +02:00
return true ;
} ) ;
2018-11-05 10:29:07 +01:00
if ( ! fOk ) {
2017-07-25 12:57:26 +02:00
// no such node? maybe this client disconnected or our own connection went down
+ + nDisconnected ;
}
}
if ( nDisconnected = = 0 ) return ; // all is clear
// smth went wrong
LogPrintf ( " CPrivateSendServer::%s -- can't continue, %llu client(s) disconnected, nSessionID: %d nSessionDenom: %d (%s) \n " ,
2018-11-05 10:29:07 +01:00
__func__ , nDisconnected , nSessionID , nSessionDenom , CPrivateSend : : GetDenominationsToString ( nSessionDenom ) ) ;
2017-07-25 12:57:26 +02:00
// notify everyone else that this session should be terminated
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries ) {
2017-09-19 16:51:38 +02:00
connman . ForNode ( entry . addr , [ & connman , this ] ( CNode * pnode ) {
PushStatus ( pnode , STATUS_REJECTED , MSG_NOERR , connman ) ;
2017-07-25 12:57:26 +02:00
return true ;
} ) ;
}
2018-11-05 10:29:07 +01:00
if ( nDisconnected = = vecEntries . size ( ) ) {
2017-07-25 12:57:26 +02:00
// all clients disconnected, there is probably some issues with our own connection
// do not charge any fees, just reset the pool
SetNull ( ) ;
}
2017-05-05 13:26:27 +02:00
}
2017-09-19 16:51:38 +02:00
void CPrivateSendServer : : RelayCompletedTransaction ( PoolMessage nMessageID , CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2017-07-25 12:57:26 +02:00
LogPrint ( " privatesend " , " CPrivateSendServer::%s -- nSessionID: %d nSessionDenom: %d (%s) \n " ,
2018-11-05 10:29:07 +01:00
__func__ , nSessionID , nSessionDenom , CPrivateSend : : GetDenominationsToString ( nSessionDenom ) ) ;
2017-07-25 12:57:26 +02:00
// final mixing tx with empty signatures should be relayed to mixing participants only
2018-02-06 12:09:33 +01:00
for ( const auto & entry : vecEntries ) {
2017-09-19 16:51:38 +02:00
bool fOk = connman . ForNode ( entry . addr , [ & nMessageID , & connman , this ] ( CNode * pnode ) {
2016-11-25 20:01:56 +01:00
CNetMsgMaker msgMaker ( pnode - > GetSendVersion ( ) ) ;
connman . PushMessage ( pnode , msgMaker . Make ( NetMsgType : : DSCOMPLETE , nSessionID , ( int ) nMessageID ) ) ;
2017-07-25 12:57:26 +02:00
return true ;
} ) ;
2018-11-05 10:29:07 +01:00
if ( ! fOk ) {
2017-07-25 12:57:26 +02:00
// no such node? maybe client disconnected or our own connection went down
2017-09-19 16:51:38 +02:00
RelayStatus ( STATUS_REJECTED , connman ) ;
2017-07-25 12:57:26 +02:00
break ;
}
}
2017-05-05 13:26:27 +02:00
}
void CPrivateSendServer : : SetState ( PoolState nStateNew )
{
2018-11-05 10:29:07 +01:00
if ( ! fMasternodeMode ) return ;
2018-03-21 12:10:01 +01:00
2018-11-05 10:29:07 +01:00
if ( nStateNew = = POOL_STATE_ERROR | | nStateNew = = POOL_STATE_SUCCESS ) {
2017-05-05 13:26:27 +02:00
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 ;
}
2018-07-16 14:47:37 +02:00
void CPrivateSendServer : : DoMaintenance ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
2018-11-05 10:29:07 +01:00
if ( fLiteMode ) return ; // disable all Dash specific functionality
if ( ! fMasternodeMode ) return ; // only run on masternodes
2017-05-05 13:26:27 +02:00
2018-11-05 10:29:07 +01:00
if ( ! masternodeSync . IsBlockchainSynced ( ) | | ShutdownRequested ( ) )
2018-07-16 14:47:37 +02:00
return ;
2017-05-05 13:26:27 +02:00
2018-07-16 14:47:37 +02:00
privateSendServer . CheckTimeout ( connman ) ;
privateSendServer . CheckForCompleteQueue ( connman ) ;
2017-05-05 13:26:27 +02:00
}