2016-02-02 16:28:56 +01:00
// Copyright (c) 2014-2016 The Dash Core developers
2015-02-04 14:24:56 +01:00
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
# include "activemasternode.h"
# include "coincontrol.h"
2016-02-02 16:28:56 +01:00
# include "consensus/validation.h"
2014-12-09 02:17:57 +01:00
# include "darksend.h"
# include "init.h"
2016-08-05 21:49:45 +02:00
# include "instantx.h"
2016-01-24 20:05:31 +01:00
# include "masternode-payments.h"
# include "masternode-sync.h"
2015-02-23 21:01:21 +01:00
# include "masternodeman.h"
2015-04-03 00:51:08 +02:00
# include "script/sign.h"
2016-03-27 20:13:08 +02:00
# include "txmempool.h"
2016-08-05 21:49:45 +02:00
# include "util.h"
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
# include <boost/lexical_cast.hpp>
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
int nPrivateSendRounds = DEFAULT_PRIVATESEND_ROUNDS ;
int nPrivateSendAmount = DEFAULT_PRIVATESEND_AMOUNT ;
int nLiquidityProvider = 0 ;
bool fEnablePrivateSend = false ;
bool fPrivateSendMultiSession = DEFAULT_PRIVATESEND_MULTISESSION ;
2014-12-09 02:17:57 +01:00
2015-03-02 00:09:33 +01:00
CDarksendPool darkSendPool ;
2014-12-09 02:17:57 +01:00
CDarkSendSigner darkSendSigner ;
2016-08-05 21:49:45 +02:00
std : : map < uint256 , CDarksendBroadcastTx > mapDarksendBroadcastTxes ;
std : : vector < CAmount > vecPrivateSendDenominations ;
2014-12-09 02:17:57 +01:00
2016-05-24 23:16:42 +02:00
void CDarksendPool : : ProcessMessage ( CNode * pfrom , std : : string & strCommand , CDataStream & vRecv )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
if ( fLiteMode ) return ; // ignore all Dash related functionality
2015-08-07 06:48:55 +02:00
if ( ! masternodeSync . IsBlockchainSynced ( ) ) return ;
2015-01-18 16:28:16 +01:00
2016-08-05 21:49:45 +02:00
if ( strCommand = = NetMsgType : : DSACCEPT ) {
2015-04-15 00:40:28 +02:00
2016-08-05 21:49:45 +02:00
int nErrorID ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
nErrorID = ERR_VERSION ;
LogPrintf ( " CDarksendPool::ProcessMessage -- DSACCEPT -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( ! fMasterNode ) {
nErrorID = ERR_NOT_A_MN ;
LogPrintf ( " CDarksendPool::ProcessMessage -- DSACCEPT -- not a Masternode! \n " ) ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
int nDenom ;
CTransaction txCollateral ;
vRecv > > nDenom > > txCollateral ;
2015-02-25 12:54:03 +01:00
CMasternode * pmn = mnodeman . Find ( activeMasternode . vin ) ;
2016-08-05 21:49:45 +02:00
if ( pmn = = NULL ) {
nErrorID = ERR_MN_LIST ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-25 20:21:35 +01:00
return ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( nSessionUsers = = 0 & & pmn - > nLastDsq ! = 0 & &
pmn - > nLastDsq + mnodeman . CountEnabled ( MIN_PRIVATESEND_PEER_PROTO_VERSION ) / 5 > mnodeman . nDsqCount )
{
LogPrintf ( " CDarksendPool::ProcessMessage -- DSACCEPT -- last dsq too recent, must wait: addr=%s \n " , pfrom - > addr . ToString ( ) ) ;
nErrorID = ERR_RECENT ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
return ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( IsDenomCompatibleWithSession ( nDenom , txCollateral , nErrorID ) ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSACCEPT -- is compatible, please submit! \n " ) ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_ACCEPTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
} else {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::ProcessMessage -- DSACCEPT -- not compatible with existing transactions! \n " ) ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSQUEUE ) {
2015-03-22 04:26:48 +01:00
TRY_LOCK ( cs_darksend , lockRecv ) ;
if ( ! lockRecv ) return ;
2015-01-19 22:25:03 +01:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSQUEUE -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2014-12-09 02:17:57 +01:00
return ;
}
CDarksendQueue dsq ;
vRecv > > dsq ;
CService addr ;
2016-08-05 21:49:45 +02:00
if ( ! dsq . GetAddress ( addr ) | | ! dsq . CheckSignature ( ) | | dsq . IsExpired ( ) ) return ;
2014-12-09 02:17:57 +01:00
2015-02-25 12:54:03 +01:00
CMasternode * pmn = mnodeman . Find ( dsq . vin ) ;
if ( pmn = = NULL ) return ;
2014-12-09 02:17:57 +01:00
// if the queue is ready, submit if we can
2016-08-05 21:49:45 +02:00
if ( dsq . fReady ) {
2015-03-02 00:09:33 +01:00
if ( ! pSubmittedToMasternode ) return ;
2016-08-05 21:49:45 +02:00
if ( ( CNetAddr ) pSubmittedToMasternode - > addr ! = ( CNetAddr ) addr ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSQUEUE -- message doesn't match current Masternode: pSubmittedToMasternode=%s, addr=%s \n " , pSubmittedToMasternode - > addr . ToString ( ) , addr . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_QUEUE ) {
2016-08-29 21:17:00 +02:00
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSQUEUE -- PrivateSend queue (%s) is ready on masternode %s \n " , dsq . ToString ( ) , addr . ToString ( ) ) ;
2016-09-02 14:19:29 +02:00
SubmitDenominate ( ) ;
2015-03-02 00:09:33 +01:00
}
2014-12-09 02:17:57 +01:00
} else {
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CDarksendQueue q , vecDarksendQueue )
2014-12-09 02:17:57 +01:00
if ( q . vin = = dsq . vin ) return ;
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d \n " , pmn - > nLastDsq , pmn - > nLastDsq + mnodeman . size ( ) / 5 , mnodeman . nDsqCount ) ;
2014-12-09 02:17:57 +01:00
//don't allow a few nodes to dominate the queuing process
2015-02-25 12:54:03 +01:00
if ( pmn - > nLastDsq ! = 0 & &
2016-08-05 21:49:45 +02:00
pmn - > nLastDsq + mnodeman . CountEnabled ( MIN_PRIVATESEND_PEER_PROTO_VERSION ) / 5 > mnodeman . nDsqCount ) {
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSQUEUE -- Masternode %s is sending too many dsq messages \n " , pmn - > addr . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2015-03-06 18:25:48 +01:00
mnodeman . nDsqCount + + ;
pmn - > nLastDsq = mnodeman . nDsqCount ;
2015-02-25 12:54:03 +01:00
pmn - > allowFreeTx = true ;
2014-12-09 02:17:57 +01:00
2016-08-29 21:17:00 +02:00
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSQUEUE -- new PrivateSend queue (%s) from masternode %s \n " , dsq . ToString ( ) , addr . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
vecDarksendQueue . push_back ( dsq ) ;
dsq . Relay ( ) ;
2016-08-05 21:49:45 +02:00
dsq . nTime = GetTime ( ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSVIN ) {
int nErrorID ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
nErrorID = ERR_VERSION ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( ! fMasterNode ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- not a Masternode! \n " ) ;
nErrorID = ERR_NOT_A_MN ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-29 21:14:34 +02:00
CDarkSendEntry entry ;
vRecv > > entry ;
2014-12-09 02:17:57 +01:00
//do we have enough users in the current session?
2016-08-05 21:49:45 +02:00
if ( nSessionUsers < GetMaxPoolTransactions ( ) ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- session not complete! \n " ) ;
nErrorID = ERR_SESSION ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
//do we have the same denominations as the current session?
2016-08-29 21:14:34 +02:00
if ( ! IsOutputsCompatibleWithSessionDenom ( entry . vecTxDSOut ) ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- not compatible with existing transactions! \n " ) ;
nErrorID = ERR_EXISTING_TX ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2014-12-25 20:21:35 +01:00
2014-12-09 02:17:57 +01:00
//check it like a transaction
{
2016-01-24 05:21:14 +01:00
CAmount nValueIn = 0 ;
CAmount nValueOut = 0 ;
2016-08-05 21:49:45 +02:00
bool fMissingTx = false ;
2014-12-09 02:17:57 +01:00
2015-04-03 00:51:08 +02:00
CMutableTransaction tx ;
2014-12-09 02:17:57 +01:00
2016-08-29 21:14:34 +02:00
BOOST_FOREACH ( const CTxOut txout , entry . vecTxDSOut ) {
2016-08-05 21:49:45 +02:00
nValueOut + = txout . nValue ;
tx . vout . push_back ( txout ) ;
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
if ( txout . scriptPubKey . size ( ) ! = 25 ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- non-standard pubkey detected! scriptPubKey=%s \n " , ScriptToAsmStr ( txout . scriptPubKey ) ) ;
nErrorID = ERR_NON_STANDARD_PUBKEY ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( ! txout . scriptPubKey . IsNormalPaymentScript ( ) ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- invalid script! scriptPubKey=%s \n " , ScriptToAsmStr ( txout . scriptPubKey ) ) ;
nErrorID = ERR_INVALID_SCRIPT ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
}
2016-08-29 21:14:34 +02:00
BOOST_FOREACH ( const CTxIn txin , entry . vecTxDSIn ) {
2016-08-05 21:49:45 +02:00
tx . vin . push_back ( txin ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSVIN -- txin=%s \n " , txin . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CTransaction txPrev ;
2014-12-09 02:17:57 +01:00
uint256 hash ;
2016-08-05 21:49:45 +02:00
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 ;
2014-12-09 02:17:57 +01:00
}
}
2016-09-01 09:03:47 +02:00
if ( nValueIn > PRIVATESEND_POOL_MAX ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- more than PrivateSend pool max! nValueIn: %lld, tx=%s " , nValueIn , tx . ToString ( ) ) ;
nErrorID = ERR_MAXIMUM ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( fMissingTx ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- missing input! tx=%s " , tx . ToString ( ) ) ;
nErrorID = ERR_MISSING_TX ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
return ;
}
// Allow lowest denom (at max) as a a fee. Normally shouldn't happen though.
// TODO: Or do not allow fees at all?
if ( nValueIn - nValueOut > vecPrivateSendDenominations . back ( ) ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- fees are too high! fees: %lld, tx=%s " , nValueIn - nValueOut , tx . ToString ( ) ) ;
nErrorID = ERR_FEES ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2015-07-30 15:44:18 +02:00
{
LOCK ( cs_main ) ;
2016-03-27 20:13:08 +02:00
CValidationState validationState ;
mempool . PrioritiseTransaction ( tx . GetHash ( ) , tx . GetHash ( ) . ToString ( ) , 1000 , 0.1 * COIN ) ;
if ( ! AcceptToMemoryPool ( mempool , validationState , CTransaction ( tx ) , false , NULL , false , true , true ) ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::ProcessMessage -- DSVIN -- transaction not valid! tx=%s " , tx . ToString ( ) ) ;
nErrorID = ERR_INVALID_TX ;
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2015-07-30 15:44:18 +02:00
return ;
}
2014-12-09 02:17:57 +01:00
}
}
2016-08-29 21:14:34 +02:00
if ( AddEntry ( entry , nErrorID ) ) {
2016-08-05 21:49:45 +02:00
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_ACCEPTED , nErrorID ) ;
CheckPool ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
RelayStatus ( MASTERNODE_RESET ) ;
2014-12-09 02:17:57 +01:00
} else {
2016-08-05 21:49:45 +02:00
pfrom - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , GetState ( ) , GetEntriesCount ( ) , MASTERNODE_REJECTED , nErrorID ) ;
2014-12-09 02:17:57 +01:00
}
2015-03-04 19:17:30 +01:00
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSSTATUSUPDATE ) {
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSSTATUSUPDATE -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2015-03-02 00:09:33 +01:00
if ( ! pSubmittedToMasternode ) return ;
2016-08-05 21:49:45 +02:00
if ( ( CNetAddr ) pSubmittedToMasternode - > addr ! = ( CNetAddr ) pfrom - > addr ) {
//LogPrintf("CDarksendPool::ProcessMessage -- DSSTATUSUPDATE -- message doesn't match current Masternode: pSubmittedToMasternode %s addr %s\n", pSubmittedToMasternode->addr.ToString(), pfrom->addr.ToString());
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
int nMsgSessionID ;
int nMsgState ;
int nMsgEntriesCount ;
int nMsgAccepted ;
int nMsgErrorID ;
vRecv > > nMsgSessionID > > nMsgState > > nMsgEntriesCount > > nMsgAccepted > > nMsgErrorID ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSSTATUSUPDATE -- nMsgState: %d nEntriesCount: %d nMsgAccepted: %d error: %s \n " , nMsgState , nEntriesCount , nMsgAccepted , GetMessageByID ( nMsgErrorID ) ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( ( nMsgAccepted ! = 1 & & nMsgAccepted ! = 0 ) & & nSessionID ! = nMsgSessionID ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSSTATUSUPDATE -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d \n " , nSessionID , nMsgSessionID ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
UpdatePoolStateOnClient ( nMsgState , nMsgEntriesCount , nMsgAccepted , nMsgErrorID , nMsgSessionID ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSSIGNFINALTX ) {
2015-03-04 19:17:30 +01:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSSIGNFINALTX -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
std : : vector < CTxIn > vecTxIn ;
vRecv > > vecTxIn ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
bool fSuccess = true ;
int nTxInIndex = 0 ;
int nTxInsCount = ( int ) vecTxIn . size ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxIn txin , vecTxIn ) {
nTxInIndex + + ;
if ( ! AddScriptSig ( txin ) ) {
fSuccess = false ;
2016-05-24 01:23:43 +02:00
break ;
}
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSSIGNFINALTX -- AddScriptSig() %d/%d success \n " , nTxInIndex , nTxInsCount ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( fSuccess ) {
CheckPool ( ) ;
RelayStatus ( MASTERNODE_RESET ) ;
2016-05-24 01:23:43 +02:00
} else {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d \n " , nTxInIndex , nTxInsCount , nSessionID ) ;
2015-03-02 00:09:33 +01:00
}
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSFINALTX ) {
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSFINALTX -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2015-03-02 00:09:33 +01:00
return ;
}
if ( ! pSubmittedToMasternode ) return ;
2016-08-05 21:49:45 +02:00
if ( ( CNetAddr ) pSubmittedToMasternode - > addr ! = ( CNetAddr ) pfrom - > addr ) {
//LogPrintf("CDarksendPool::ProcessMessage -- DSFINALTX -- message doesn't match current Masternode: pSubmittedToMasternode %s addr %s\n", pSubmittedToMasternode->addr.ToString(), pfrom->addr.ToString());
2015-03-02 00:09:33 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
int nMsgSessionID ;
2015-03-02 00:09:33 +01:00
CTransaction txNew ;
2016-08-05 21:49:45 +02:00
vRecv > > nMsgSessionID > > txNew ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
if ( nSessionID ! = nMsgSessionID ) {
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSFINALTX -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d \n " , nSessionID , nMsgSessionID ) ;
2015-03-02 00:09:33 +01:00
return ;
}
//check to see if input is spent already? (and probably not confirmed)
SignFinalTransaction ( txNew , pfrom ) ;
2015-03-04 19:17:30 +01:00
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSCOMPLETE ) {
2015-03-04 19:17:30 +01:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
LogPrintf ( " CDarksendPool::ProcessMessage -- DSCOMPLETE -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2015-03-02 00:09:33 +01:00
return ;
}
if ( ! pSubmittedToMasternode ) return ;
2016-08-05 21:49:45 +02:00
if ( ( CNetAddr ) pSubmittedToMasternode - > addr ! = ( CNetAddr ) pfrom - > addr ) {
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSCOMPLETE -- message doesn't match current Masternode: pSubmittedToMasternode=%s addr=%s \n " , pSubmittedToMasternode - > addr . ToString ( ) , pfrom - > addr . ToString ( ) ) ;
2015-03-02 00:09:33 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
int nMsgSessionID ;
bool fMsgError ;
int nMsgErrorID ;
vRecv > > nMsgSessionID > > fMsgError > > nMsgErrorID ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
if ( nSessionID ! = nMsgSessionID ) {
LogPrint ( " privatesend " , " CDarksendPool::ProcessMessage -- DSCOMPLETE -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d \n " , darkSendPool . nSessionID , nMsgSessionID ) ;
2015-03-02 00:09:33 +01:00
return ;
2014-12-09 02:17:57 +01:00
}
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
CompletedTransaction ( fMsgError , nMsgErrorID ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
void CDarksendPool : : InitDenominations ( )
{
vecPrivateSendDenominations . clear ( ) ;
/* Denominations
A note about convertability . Within mixing pools , each denomination
is convertable to another .
For example :
1 DRK + 1000 = = ( .1 DRK + 100 ) * 10
10 DRK + 10000 = = ( 1 DRK + 1000 ) * 10
*/
vecPrivateSendDenominations . push_back ( ( 100 * COIN ) + 100000 ) ;
vecPrivateSendDenominations . push_back ( ( 10 * COIN ) + 10000 ) ;
vecPrivateSendDenominations . push_back ( ( 1 * COIN ) + 1000 ) ;
vecPrivateSendDenominations . push_back ( ( .1 * COIN ) + 100 ) ;
/* Disabled till we need them
vecPrivateSendDenominations . push_back ( ( .01 * COIN ) + 10 ) ;
vecPrivateSendDenominations . push_back ( ( .001 * COIN ) + 1 ) ;
*/
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
void CDarksendPool : : ResetPool ( )
{
nCachedLastSuccessBlock = 0 ;
txMyCollateral = CMutableTransaction ( ) ;
2014-12-28 15:46:39 +01:00
vecMasternodesUsed . clear ( ) ;
2015-01-08 22:16:17 +01:00
UnlockCoins ( ) ;
2014-12-28 15:46:39 +01:00
SetNull ( ) ;
}
2016-08-05 21:49:45 +02:00
void CDarksendPool : : SetNull ( )
{
2015-07-26 00:43:40 +02:00
// MN side
2016-08-05 21:49:45 +02:00
nSessionUsers = 0 ;
2015-07-26 00:43:40 +02:00
vecSessionCollateral . clear ( ) ;
2014-12-09 02:17:57 +01:00
2015-07-26 00:43:40 +02:00
// Client side
2016-08-05 21:49:45 +02:00
nEntriesCount = 0 ;
fLastEntryAccepted = false ;
nAcceptedEntriesCount = 0 ;
fSessionFoundMasternode = false ;
2014-12-09 02:17:57 +01:00
2015-07-26 00:43:40 +02:00
// Both sides
2016-08-05 21:49:45 +02:00
nState = POOL_STATE_IDLE ;
nSessionID = 0 ;
nSessionDenom = 0 ;
vecEntries . clear ( ) ;
2016-03-27 20:13:08 +02:00
finalMutableTransaction . vin . clear ( ) ;
finalMutableTransaction . vout . clear ( ) ;
2016-08-05 21:49:45 +02:00
nLastTimeChanged = GetTimeMillis ( ) ;
2014-12-09 02:17:57 +01:00
}
//
2016-08-05 21:49:45 +02:00
// Unlock coins after mixing fails or succeeds
2014-12-09 02:17:57 +01:00
//
2016-08-05 21:49:45 +02:00
void CDarksendPool : : UnlockCoins ( )
{
2015-08-07 06:08:37 +02:00
while ( true ) {
TRY_LOCK ( pwalletMain - > cs_wallet , lockWallet ) ;
2015-08-08 23:48:09 +02:00
if ( ! lockWallet ) { MilliSleep ( 50 ) ; continue ; }
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( COutPoint outpoint , vecOutPointLocked )
pwalletMain - > UnlockCoin ( outpoint ) ;
2015-08-07 06:08:37 +02:00
break ;
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
vecOutPointLocked . clear ( ) ;
2014-12-09 02:17:57 +01:00
}
2015-06-24 18:08:14 +02:00
std : : string CDarksendPool : : GetStatus ( )
{
2016-08-05 21:49:45 +02:00
static int nStatusMessageProgress = 0 ;
nStatusMessageProgress + = 10 ;
std : : string strSuffix = " " ;
2015-06-24 18:08:14 +02:00
2016-08-05 21:49:45 +02:00
if ( ( pCurrentBlockIndex & & pCurrentBlockIndex - > nHeight - nCachedLastSuccessBlock < nMinBlockSpacing ) | | ! masternodeSync . IsBlockchainSynced ( ) )
2015-06-24 18:08:14 +02:00
return strAutoDenomResult ;
2016-08-05 21:49:45 +02:00
switch ( nState ) {
case POOL_STATE_IDLE :
2016-05-09 21:08:13 +02:00
return _ ( " PrivateSend is idle. " ) ;
2016-08-05 21:49:45 +02:00
case POOL_STATE_ACCEPTING_ENTRIES :
if ( nEntriesCount = = 0 ) {
nStatusMessageProgress = 0 ;
2015-06-24 18:08:14 +02:00
return strAutoDenomResult ;
2016-08-05 21:49:45 +02:00
} else if ( fLastEntryAccepted ) {
if ( nStatusMessageProgress % 10 > 8 ) {
fLastEntryAccepted = false ;
nStatusMessageProgress = 0 ;
2015-06-24 18:08:14 +02:00
}
2016-05-09 21:08:13 +02:00
return _ ( " PrivateSend request complete: " ) + " " + _ ( " Your transaction was accepted into the pool! " ) ;
2015-06-24 18:08:14 +02:00
} else {
2016-08-05 21:49:45 +02:00
if ( nStatusMessageProgress % 70 < = 40 ) return strprintf ( _ ( " Submitted following entries to masternode: %u / %d " ) , nEntriesCount , GetMaxPoolTransactions ( ) ) ;
else if ( nStatusMessageProgress % 70 < = 50 ) strSuffix = " . " ;
else if ( nStatusMessageProgress % 70 < = 60 ) strSuffix = " .. " ;
else if ( nStatusMessageProgress % 70 < = 70 ) strSuffix = " ... " ;
return strprintf ( _ ( " Submitted to masternode, waiting for more entries ( %u / %d ) % s " ), nEntriesCount, GetMaxPoolTransactions(), strSuffix) ;
2015-06-24 18:08:14 +02:00
}
2016-08-05 21:49:45 +02:00
case POOL_STATE_SIGNING :
if ( nStatusMessageProgress % 70 < = 40 ) return _ ( " Found enough users, signing ... " ) ;
else if ( nStatusMessageProgress % 70 < = 50 ) strSuffix = " . " ;
else if ( nStatusMessageProgress % 70 < = 60 ) strSuffix = " .. " ;
else if ( nStatusMessageProgress % 70 < = 70 ) strSuffix = " ... " ;
return strprintf ( _ ( " Found enough users, signing ( waiting %s ) " ), strSuffix) ;
case POOL_STATE_TRANSMISSION :
2015-06-24 18:08:14 +02:00
return _ ( " Transmitting final transaction. " ) ;
2016-08-05 21:49:45 +02:00
case POOL_STATE_FINALIZE_TRANSACTION :
2015-06-24 18:08:14 +02:00
return _ ( " Finalizing transaction. " ) ;
2016-08-05 21:49:45 +02:00
case POOL_STATE_ERROR :
return _ ( " PrivateSend request incomplete: " ) + " " + strLastMessage + " " + _ ( " Will retry... " ) ;
case POOL_STATE_SUCCESS :
return _ ( " PrivateSend request complete: " ) + " " + strLastMessage ;
case POOL_STATE_QUEUE :
if ( nStatusMessageProgress % 70 < = 30 ) strSuffix = " . " ;
else if ( nStatusMessageProgress % 70 < = 50 ) strSuffix = " .. " ;
else if ( nStatusMessageProgress % 70 < = 70 ) strSuffix = " ... " ;
return strprintf ( _ ( " Submitted to masternode, waiting in queue %s " ) , strSuffix ) ; ;
2015-06-24 18:08:14 +02:00
default :
2016-08-05 21:49:45 +02:00
return strprintf ( _ ( " Unknown state: id = %u " ) , nState ) ;
2015-06-24 18:08:14 +02:00
}
}
2014-12-09 02:17:57 +01:00
//
2016-08-05 21:49:45 +02:00
// Check the mixing progress and send client updates if a Masternode
2014-12-25 20:21:35 +01:00
//
2016-08-05 21:49:45 +02:00
void CDarksendPool : : CheckPool ( )
2014-12-09 02:17:57 +01:00
{
2015-07-26 00:43:40 +02:00
if ( fMasterNode ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- entries count %lu \n " , GetEntriesCount ( ) ) ;
2015-07-26 00:43:40 +02:00
// If entries is full, then move on to the next phase
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES & & GetEntriesCount ( ) > = GetMaxPoolTransactions ( ) ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- TRYING TRANSACTION \n " ) ;
SetState ( POOL_STATE_FINALIZE_TRANSACTION ) ;
2015-07-26 00:43:40 +02:00
}
2014-12-09 02:17:57 +01:00
}
// create the finalized transaction for distribution to the clients
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_FINALIZE_TRANSACTION ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- FINALIZE TRANSACTIONS \n " ) ;
SetState ( POOL_STATE_SIGNING ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( ! fMasterNode ) return ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CMutableTransaction txNew ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
// make our new transaction
for ( int i = 0 ; i < GetEntriesCount ( ) ; i + + ) {
BOOST_FOREACH ( const CTxDSOut & txdsout , vecEntries [ i ] . vecTxDSOut )
txNew . vout . push_back ( txdsout ) ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxDSIn & txdsin , vecEntries [ i ] . vecTxDSIn )
txNew . vin . push_back ( txdsin ) ;
}
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
// 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 ( ) ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
finalMutableTransaction = txNew ;
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- finalMutableTransaction=%s " , txNew . ToString ( ) ) ;
2015-03-04 19:17:30 +01:00
2016-08-05 21:49:45 +02:00
// request signatures from clients
RelayFinalTransaction ( finalMutableTransaction ) ;
2014-12-09 02:17:57 +01:00
}
// If we have all of the signatures, try to compile the transaction
2016-08-05 21:49:45 +02:00
if ( fMasterNode & & nState = = POOL_STATE_SIGNING & & IsSignaturesComplete ( ) ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- SIGNING \n " ) ;
SetState ( POOL_STATE_TRANSMISSION ) ;
2015-03-02 00:09:33 +01:00
CheckFinalTransaction ( ) ;
}
2014-12-09 02:17:57 +01:00
2015-03-02 00:09:33 +01:00
// reset if we're here for 10 seconds
2016-08-05 21:49:45 +02:00
if ( ( nState = = POOL_STATE_ERROR | | nState = = POOL_STATE_SUCCESS ) & & GetTimeMillis ( ) - nLastTimeChanged > = 10000 ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- timeout, RESETTING \n " ) ;
2015-03-02 00:09:33 +01:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2016-08-05 21:49:45 +02:00
if ( fMasterNode ) RelayStatus ( MASTERNODE_RESET ) ;
2015-03-02 00:09:33 +01:00
}
}
2014-12-09 02:17:57 +01:00
2015-03-02 00:09:33 +01:00
void CDarksendPool : : CheckFinalTransaction ( )
{
2016-08-05 21:49:45 +02:00
if ( ! fMasterNode ) return ; // check and relay final tx only on masternode
2015-07-26 00:43:40 +02:00
2016-03-27 20:13:08 +02:00
CTransaction finalTransaction = CTransaction ( finalMutableTransaction ) ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::CheckFinalTransaction -- finalTransaction=%s " , finalTransaction . ToString ( ) ) ;
2015-03-02 00:09:33 +01:00
2016-03-02 21:26:45 +01:00
{
2015-07-26 00:43:40 +02:00
// See if the transaction is valid
2016-03-27 20:13:08 +02:00
TRY_LOCK ( cs_main , lockMain ) ;
CValidationState validationState ;
mempool . PrioritiseTransaction ( finalTransaction . GetHash ( ) , finalTransaction . GetHash ( ) . ToString ( ) , 1000 , 0.1 * COIN ) ;
if ( ! lockMain | | ! AcceptToMemoryPool ( mempool , validationState , finalTransaction , false , NULL , false , true , true ) )
2015-07-26 00:43:40 +02:00
{
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::CheckFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid \n " ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2014-12-09 02:17:57 +01:00
2015-07-26 00:43:40 +02:00
// not much we can do in this case
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_ACCEPTING_ENTRIES ) ;
RelayCompletedTransaction ( true , ERR_INVALID_TX ) ;
2015-07-26 00:43:40 +02:00
return ;
}
2016-03-02 21:26:45 +01:00
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::CheckFinalTransaction -- CREATING DSTX \n " ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
// create and sign masternode dstx transaction
if ( ! mapDarksendBroadcastTxes . count ( finalTransaction . GetHash ( ) ) ) {
CDarksendBroadcastTx dstx ( finalTransaction , activeMasternode . vin , GetAdjustedTime ( ) ) ;
dstx . Sign ( ) ;
mapDarksendBroadcastTxes . insert ( std : : make_pair ( finalTransaction . GetHash ( ) , dstx ) ) ;
2016-03-02 21:26:45 +01:00
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::CheckFinalTransaction -- TRANSMITTING DSTX \n " ) ;
2014-12-09 02:17:57 +01:00
2016-03-27 20:13:08 +02:00
CInv inv ( MSG_DSTX , finalTransaction . GetHash ( ) ) ;
2016-03-02 21:26:45 +01:00
RelayInv ( inv ) ;
2014-12-09 02:17:57 +01:00
2016-03-02 21:26:45 +01:00
// Tell the clients it was successful
2016-08-05 21:49:45 +02:00
RelayCompletedTransaction ( false , MSG_SUCCESS ) ;
2015-03-02 00:09:33 +01:00
2016-03-02 21:26:45 +01:00
// Randomly charge clients
ChargeRandomFees ( ) ;
2015-03-02 00:09:33 +01:00
2016-03-02 21:26:45 +01:00
// Reset
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::CheckFinalTransaction -- COMPLETED -- RESETTING \n " ) ;
2016-03-02 21:26:45 +01:00
SetNull ( ) ;
2016-08-05 21:49:45 +02:00
RelayStatus ( MASTERNODE_RESET ) ;
2014-12-09 02:17:57 +01:00
}
//
// Charge clients a fee if they're abusive
//
2016-09-01 09:03:47 +02:00
// 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
2014-12-09 02:17:57 +01:00
// 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.
2014-12-25 20:21:35 +01:00
//
2016-02-17 23:18:57 +01:00
// How does this work? Messages to Masternodes come in via NetMsgType::DSVIN, these require a valid collateral
2015-03-05 09:10:15 +01:00
// transaction for the client to be able to enter the pool. This transaction is kept by the Masternode
2014-12-25 20:21:35 +01:00
// until the transaction is either complete or fails.
2014-12-09 02:17:57 +01:00
//
2016-08-05 21:49:45 +02:00
void CDarksendPool : : ChargeFees ( )
{
2015-07-26 00:43:40 +02:00
if ( ! fMasterNode ) return ;
2014-12-09 02:17:57 +01:00
2015-07-26 00:43:40 +02:00
//we don't need to charge collateral for every offence.
2016-08-05 21:49:45 +02:00
int nOffences = 0 ;
int r = insecure_rand ( ) % 100 ;
2015-07-26 00:43:40 +02:00
if ( r > 33 ) return ;
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES ) {
2015-07-26 00:43:40 +02:00
BOOST_FOREACH ( const CTransaction & txCollateral , vecSessionCollateral ) {
2016-08-05 21:49:45 +02:00
bool fFound = false ;
BOOST_FOREACH ( const CDarkSendEntry & entry , vecEntries )
if ( entry . txCollateral = = txCollateral )
fFound = true ;
2015-07-26 00:43:40 +02:00
// This queue entry didn't send us the promised transaction
2016-08-05 21:49:45 +02:00
if ( ! fFound ) {
2015-07-26 00:43:40 +02:00
LogPrintf ( " CDarksendPool::ChargeFees -- found uncooperative node (didn't send transaction). Found offence. \n " ) ;
2016-08-05 21:49:45 +02:00
nOffences + + ;
2015-07-26 00:43:40 +02:00
}
2014-12-09 02:17:57 +01:00
}
2015-07-26 00:43:40 +02:00
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_SIGNING ) {
2015-07-26 00:43:40 +02:00
// who didn't sign?
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CDarkSendEntry entry , vecEntries ) {
BOOST_FOREACH ( const CTxDSIn txdsin , entry . vecTxDSIn ) {
if ( ! txdsin . fHasSig ) {
2015-07-26 00:43:40 +02:00
LogPrintf ( " CDarksendPool::ChargeFees -- found uncooperative node (didn't sign). Found offence \n " ) ;
2016-08-05 21:49:45 +02:00
nOffences + + ;
2014-12-09 02:17:57 +01:00
}
}
}
2015-07-26 00:43:40 +02:00
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
//pick random client to charge
r = insecure_rand ( ) % 100 ;
int nTarget = 0 ;
2014-12-09 02:17:57 +01:00
2015-07-26 00:43:40 +02:00
//mostly offending?
2016-08-05 21:49:45 +02:00
if ( nOffences > = Params ( ) . PoolMaxTransactions ( ) - 1 & & r > 33 ) return ;
2014-12-09 02:17:57 +01:00
2015-07-26 00:43:40 +02:00
//everyone is an offender? That's not right
2016-08-05 21:49:45 +02:00
if ( nOffences > = Params ( ) . PoolMaxTransactions ( ) ) return ;
2014-12-09 02:17:57 +01:00
2015-07-26 00:43:40 +02:00
//charge one of the offenders randomly
2016-08-05 21:49:45 +02:00
if ( nOffences > 1 ) nTarget = 50 ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES ) {
2015-07-26 00:43:40 +02:00
BOOST_FOREACH ( const CTransaction & txCollateral , vecSessionCollateral ) {
2016-08-05 21:49:45 +02:00
bool fFound = false ;
BOOST_FOREACH ( const CDarkSendEntry & entry , vecEntries )
if ( entry . txCollateral = = txCollateral )
fFound = true ;
2015-07-26 00:43:40 +02:00
// This queue entry didn't send us the promised transaction
2016-08-05 21:49:45 +02:00
if ( ! fFound & & r > nTarget ) {
2015-07-26 00:43:40 +02:00
LogPrintf ( " CDarksendPool::ChargeFees -- found uncooperative node (didn't send transaction). charging fees. \n " ) ;
CWalletTx wtxCollateral = CWalletTx ( pwalletMain , txCollateral ) ;
// Broadcast
2016-08-05 21:49:45 +02:00
// This must not fail. The transaction has already been signed and recorded.
if ( ! wtxCollateral . AcceptToMemoryPool ( true ) ) {
LogPrintf ( " CDarksendPool::ChargeFees -- Error: Transaction not valid \n " ) ;
return ;
2014-12-09 02:17:57 +01:00
}
2015-07-26 00:43:40 +02:00
wtxCollateral . RelayWalletTransaction ( ) ;
return ;
}
}
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_SIGNING ) {
2015-07-26 00:43:40 +02:00
// who didn't sign?
2016-08-29 21:14:34 +02:00
BOOST_FOREACH ( const CDarkSendEntry & entry , vecEntries ) {
BOOST_FOREACH ( const CTxDSIn & txdsin , entry . vecTxDSIn ) {
2016-08-05 21:49:45 +02:00
if ( ! txdsin . fHasSig & & r > nTarget ) {
2015-07-26 00:43:40 +02:00
LogPrintf ( " CDarksendPool::ChargeFees -- found uncooperative node (didn't sign). charging fees. \n " ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CWalletTx wtxCollateral = CWalletTx ( pwalletMain , entry . txCollateral ) ;
2014-12-09 02:17:57 +01:00
// Broadcast
2016-08-05 21:49:45 +02:00
if ( ! wtxCollateral . AcceptToMemoryPool ( false ) ) {
2014-12-09 02:17:57 +01:00
// This must not fail. The transaction has already been signed and recorded.
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::ChargeFees -- Error: Transaction not valid \n " ) ;
2014-12-09 02:17:57 +01:00
}
wtxCollateral . RelayWalletTransaction ( ) ;
return ;
}
}
}
}
}
2016-08-05 21:49:45 +02:00
/*
Charge the collateral randomly .
Mixing is completely free , to pay miners we randomly pay the collateral of users .
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
Collateral Fee Charges :
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
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 .
*/
void CDarksendPool : : ChargeRandomFees ( )
{
if ( ! fMasterNode ) return ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTransaction & txCollateral , vecSessionCollateral ) {
int r = insecure_rand ( ) % 100 ;
if ( r > 10 ) return ;
LogPrintf ( " CDarksendPool::ChargeRandomFees -- charging random fees, collateral, txCollateral=%s " , txCollateral . ToString ( ) ) ;
CWalletTx wtxCollateral = CWalletTx ( pwalletMain , txCollateral ) ;
// This must not fail. The transaction has already been signed and recorded.
if ( ! wtxCollateral . AcceptToMemoryPool ( true ) ) {
LogPrintf ( " CDarksendPool::ChargeRandomFees -- Error: Transaction not valid, txCollateral=%s " , txCollateral . ToString ( ) ) ;
return ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
// Broadcast
wtxCollateral . RelayWalletTransaction ( ) ;
2014-12-09 02:17:57 +01:00
}
}
//
2016-08-05 21:49:45 +02:00
// Check for various timeouts (queue objects, mixing, etc)
2014-12-09 02:17:57 +01:00
//
2016-08-05 21:49:45 +02:00
void CDarksendPool : : CheckTimeout ( )
{
// check mixing queue objects for timeouts
std : : vector < CDarksendQueue > : : iterator it = vecDarksendQueue . begin ( ) ;
while ( it ! = vecDarksendQueue . end ( ) ) {
if ( ( * it ) . IsExpired ( ) ) {
2016-08-29 21:17:00 +02:00
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Removing expired queue (%s) \n " , ( * it ) . ToString ( ) ) ;
2016-03-08 12:09:22 +01:00
it = vecDarksendQueue . erase ( it ) ;
} else + + it ;
}
2016-05-25 07:25:16 +02:00
if ( ! fEnablePrivateSend & & ! fMasterNode ) return ;
2015-01-20 11:32:32 +01:00
2014-12-09 02:17:57 +01:00
// catching hanging sessions
if ( ! fMasterNode ) {
2016-08-05 21:49:45 +02:00
switch ( nState ) {
case POOL_STATE_TRANSMISSION :
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Session complete -- Running CheckPool \n " ) ;
CheckPool ( ) ;
2015-06-24 18:08:14 +02:00
break ;
2016-08-05 21:49:45 +02:00
case POOL_STATE_ERROR :
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Pool error -- Running CheckPool \n " ) ;
CheckPool ( ) ;
2015-06-24 18:08:14 +02:00
break ;
2016-08-05 21:49:45 +02:00
case POOL_STATE_SUCCESS :
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Pool success -- Running CheckPool \n " ) ;
CheckPool ( ) ;
2015-06-24 18:08:14 +02:00
break ;
2014-12-25 20:21:35 +01:00
}
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
int nLagTime = 0 ;
if ( ! fMasterNode ) nLagTime = 10000 ; //if we're the client, give the server a few extra seconds before resetting.
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES | | nState = = POOL_STATE_QUEUE ) {
2016-08-29 21:17:00 +02:00
int c = 0 ;
2014-12-09 02:17:57 +01:00
// check for a timeout and reset if needed
2016-08-05 21:49:45 +02:00
std : : vector < CDarkSendEntry > : : iterator it2 = vecEntries . begin ( ) ;
while ( it2 ! = vecEntries . end ( ) ) {
if ( ( * it2 ) . IsExpired ( ) ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Removing expired entry: %d \n " , c ) ;
it2 = vecEntries . erase ( it2 ) ;
if ( GetEntriesCount ( ) = = 0 ) {
2014-12-09 02:17:57 +01:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( fMasterNode )
RelayStatus ( MASTERNODE_RESET ) ;
2015-07-26 00:43:40 +02:00
} else + + it2 ;
2014-12-09 02:17:57 +01:00
c + + ;
}
2016-09-01 09:03:47 +02:00
if ( GetTimeMillis ( ) - nLastTimeChanged > = PRIVATESEND_QUEUE_TIMEOUT * 1000 + nLagTime ) {
2015-05-30 05:46:29 +02:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2014-12-09 02:17:57 +01:00
}
2016-09-01 09:03:47 +02:00
} else if ( GetTimeMillis ( ) - nLastTimeChanged > = PRIVATESEND_QUEUE_TIMEOUT * 1000 + nLagTime ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Session timed out (%ds) -- resetting \n " , PRIVATESEND_QUEUE_TIMEOUT ) ;
2014-12-09 02:17:57 +01:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_ERROR ) ;
strLastMessage = _ ( " Session timed out. " ) ;
2014-12-09 02:17:57 +01:00
}
2016-09-01 09:03:47 +02:00
if ( nState = = POOL_STATE_SIGNING & & GetTimeMillis ( ) - nLastTimeChanged > = PRIVATESEND_SIGNING_TIMEOUT * 1000 + nLagTime ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Session timed out (%ds) -- restting \n " , PRIVATESEND_SIGNING_TIMEOUT ) ;
2016-08-05 21:49:45 +02:00
ChargeFees ( ) ;
UnlockCoins ( ) ;
SetNull ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_ERROR ) ;
strLastMessage = _ ( " Signing timed out. " ) ;
2015-03-02 00:09:33 +01:00
}
}
2016-08-05 21:49:45 +02:00
/*
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 CDarksendPool : : CheckForCompleteQueue ( )
{
2016-05-25 07:25:16 +02:00
if ( ! fEnablePrivateSend & & ! fMasterNode ) return ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_QUEUE & & nSessionUsers = = GetMaxPoolTransactions ( ) ) {
SetState ( POOL_STATE_ACCEPTING_ENTRIES ) ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
CDarksendQueue dsq ( nSessionDenom , activeMasternode . vin , GetTime ( ) , true ) ;
2016-08-29 21:17:00 +02:00
LogPrint ( " privatesend " , " CDarksendPool::CheckForCompleteQueue -- queue is ready, signing and relaying (%s) \n " , dsq . ToString ( ) ) ;
2015-03-02 00:09:33 +01:00
dsq . Sign ( ) ;
dsq . Relay ( ) ;
2014-12-09 02:17:57 +01:00
}
}
// check to see if the signature is valid
2016-08-05 21:49:45 +02:00
bool CDarksendPool : : IsSignatureValid ( const CScript & scriptSig , const CTxIn & txin )
{
2015-04-03 00:51:08 +02:00
CMutableTransaction txNew ;
2014-12-09 02:17:57 +01:00
txNew . vin . clear ( ) ;
txNew . vout . clear ( ) ;
2016-08-05 21:49:45 +02:00
int i = 0 ;
int nTxInIndex = - 1 ;
2014-12-09 02:17:57 +01:00
CScript sigPubKey = CScript ( ) ;
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CDarkSendEntry & entry , vecEntries ) {
BOOST_FOREACH ( const CTxDSOut & txdsout , entry . vecTxDSOut )
txNew . vout . push_back ( txdsout ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxDSIn & txdsin , entry . vecTxDSIn ) {
txNew . vin . push_back ( txdsin ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( txdsin = = txin ) {
nTxInIndex = i ;
sigPubKey = txdsin . prevPubKey ;
2014-12-09 02:17:57 +01:00
}
i + + ;
}
}
2016-08-05 21:49:45 +02:00
if ( nTxInIndex > = 0 ) { //might have to do this one input at a time?
txNew . vin [ nTxInIndex ] . scriptSig = scriptSig ;
LogPrint ( " privatesend " , " CDarksendPool::IsSignatureValid -- Sign with sig %s \n " , ScriptToAsmStr ( scriptSig ) . substr ( 0 , 24 ) ) ;
if ( ! VerifyScript ( txNew . vin [ nTxInIndex ] . scriptSig , sigPubKey , SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC , MutableTransactionSignatureChecker ( & txNew , nTxInIndex ) ) ) {
LogPrint ( " privatesend " , " CDarksendPool::IsSignatureValid -- Signing -- Error signing input %d \n " , nTxInIndex ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::IsSignatureValid -- Signing -- Successfully validated input \n " ) ;
2014-12-09 02:17:57 +01:00
return true ;
}
// check to make sure the collateral provided by the client is valid
2016-08-05 21:49:45 +02:00
bool CDarksendPool : : IsCollateralValid ( const CTransaction & txCollateral )
{
2014-12-09 02:17:57 +01:00
if ( txCollateral . vout . size ( ) < 1 ) return false ;
if ( txCollateral . nLockTime ! = 0 ) return false ;
2016-01-24 05:21:14 +01:00
CAmount nValueIn = 0 ;
CAmount nValueOut = 0 ;
2016-08-05 21:49:45 +02:00
bool fMissingTx = false ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxOut txout , txCollateral . vout ) {
nValueOut + = txout . nValue ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( ! txout . scriptPubKey . IsNormalPaymentScript ( ) ) {
LogPrintf ( " CDarksendPool::IsCollateralValid -- Invalid Script, txCollateral=%s " , txCollateral . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxIn txin , txCollateral . vin ) {
CTransaction txPrev ;
2014-12-09 02:17:57 +01:00
uint256 hash ;
2016-08-05 21:49:45 +02:00
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 ;
2014-12-09 02:17:57 +01:00
}
}
2016-08-05 21:49:45 +02:00
if ( fMissingTx ) {
LogPrint ( " privatesend " , " CDarksendPool::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s " , txCollateral . ToString ( ) ) ;
2014-12-25 20:21:35 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-09-01 09:03:47 +02:00
//collateral transactions are required to pay out PRIVATESEND_COLLATERAL as a fee to the miners
if ( nValueIn - nValueOut < PRIVATESEND_COLLATERAL ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s " , nValueOut - nValueIn , txCollateral . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::IsCollateralValid -- %s " , txCollateral . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
2015-07-30 15:44:18 +02:00
{
LOCK ( cs_main ) ;
2016-03-27 20:13:08 +02:00
CValidationState validationState ;
2016-08-05 21:49:45 +02:00
if ( ! AcceptToMemoryPool ( mempool , validationState , txCollateral , false , NULL , false , true , true ) ) {
LogPrint ( " privatesend " , " CDarksendPool::IsCollateralValid -- didn't pass AcceptToMemoryPool() \n " ) ;
2015-07-30 15:44:18 +02:00
return false ;
}
2014-12-09 02:17:57 +01:00
}
return true ;
}
2014-12-25 20:21:35 +01:00
//
2014-12-09 02:17:57 +01:00
// Add a clients transaction to the pool
//
2016-08-29 21:14:34 +02:00
bool CDarksendPool : : AddEntry ( const CDarkSendEntry & entryNew , int & nErrorIDRet )
2016-08-05 21:49:45 +02:00
{
if ( ! fMasterNode ) return false ;
2016-08-29 21:14:34 +02:00
BOOST_FOREACH ( CTxIn txin , entryNew . vecTxDSIn ) {
if ( txin . prevout . IsNull ( ) ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::AddEntry -- input not valid! \n " ) ;
2016-08-29 21:14:34 +02:00
nErrorIDRet = ERR_INVALID_INPUT ;
2016-08-05 21:49:45 +02:00
nSessionUsers - - ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
2016-08-29 21:14:34 +02:00
if ( ! IsCollateralValid ( entryNew . txCollateral ) ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::AddEntry -- collateral not valid! \n " ) ;
2016-08-29 21:14:34 +02:00
nErrorIDRet = ERR_INVALID_COLLATERAL ;
2016-08-05 21:49:45 +02:00
nSessionUsers - - ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( GetEntriesCount ( ) > = GetMaxPoolTransactions ( ) ) {
LogPrint ( " privatesend " , " CDarksendPool::AddEntry -- entries is full! \n " ) ;
2016-08-29 21:14:34 +02:00
nErrorIDRet = ERR_ENTRIES_FULL ;
2016-08-05 21:49:45 +02:00
nSessionUsers - - ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-29 21:14:34 +02:00
BOOST_FOREACH ( CTxIn txin , entryNew . vecTxDSIn ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " looking for txin -- %s \n " , txin . ToString ( ) ) ;
BOOST_FOREACH ( const CDarkSendEntry & entry , vecEntries ) {
BOOST_FOREACH ( const CTxDSIn & txdsin , entry . vecTxDSIn ) {
if ( ( CTxIn ) txdsin = = txin ) {
LogPrint ( " privatesend " , " CDarksendPool::AddEntry -- found in txin \n " ) ;
2016-08-29 21:14:34 +02:00
nErrorIDRet = ERR_ALREADY_HAVE ;
2016-08-05 21:49:45 +02:00
nSessionUsers - - ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
}
}
2016-08-29 21:14:34 +02:00
vecEntries . push_back ( entryNew ) ;
2014-12-09 02:17:57 +01:00
2016-08-29 21:14:34 +02:00
LogPrint ( " privatesend " , " CDarksendPool::AddEntry -- adding entry \n " ) ;
nErrorIDRet = MSG_ENTRIES_ADDED ;
2014-12-09 02:17:57 +01:00
2015-03-02 00:09:33 +01:00
return true ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
bool CDarksendPool : : AddScriptSig ( const CTxIn & txinNew )
{
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- new sig, scriptSig=%s \n " , ScriptToAsmStr ( txinNew . scriptSig ) . substr ( 0 , 24 ) ) ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CDarkSendEntry & entry , vecEntries ) {
BOOST_FOREACH ( const CTxDSIn & txdsin , entry . vecTxDSIn ) {
if ( txdsin . scriptSig = = txinNew . scriptSig ) {
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- already exists \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
}
2016-08-05 21:49:45 +02:00
if ( ! IsSignatureValid ( txinNew . scriptSig , txinNew ) ) {
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- Invalid Sig \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- scriptSig=%s \n " , ScriptToAsmStr ( txinNew . scriptSig ) ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
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 " , " CDarksendPool::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s \n " , ScriptToAsmStr ( txinNew . scriptSig ) . substr ( 0 , 24 ) ) ;
2015-04-06 17:39:48 +02:00
}
}
2016-08-05 21:49:45 +02:00
for ( int i = 0 ; i < GetEntriesCount ( ) ; i + + ) {
if ( vecEntries [ i ] . AddScriptSig ( txinNew ) ) {
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- adding to entries, scriptSig=%s \n " , ScriptToAsmStr ( txinNew . scriptSig ) . substr ( 0 , 24 ) ) ;
2015-03-02 00:09:33 +01:00
return true ;
2014-12-09 02:17:57 +01:00
}
}
2015-03-02 00:09:33 +01:00
LogPrintf ( " CDarksendPool::AddScriptSig -- Couldn't set sig! \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2015-03-05 08:49:50 +01:00
// Check to make sure everything is signed
2016-08-05 21:49:45 +02:00
bool CDarksendPool : : IsSignaturesComplete ( )
{
BOOST_FOREACH ( const CDarkSendEntry & entry , vecEntries )
BOOST_FOREACH ( const CTxDSIn & txdsin , entry . vecTxDSIn )
if ( ! txdsin . fHasSig ) return false ;
2016-07-15 12:21:20 +02:00
2014-12-09 02:17:57 +01:00
return true ;
}
//
2016-08-05 21:49:45 +02:00
// Execute a mixing denomination via a Masternode.
2014-12-09 02:17:57 +01:00
// This is only ran from clients
2014-12-25 20:21:35 +01:00
//
2016-09-02 14:19:29 +02:00
bool CDarksendPool : : SendDenominate ( const std : : vector < CTxIn > & vecTxIn , const std : : vector < CTxOut > & vecTxOut )
2016-08-05 21:49:45 +02:00
{
2015-07-26 00:43:40 +02:00
if ( fMasterNode ) {
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SendDenominate -- PrivateSend from a Masternode is not supported currently. \n " ) ;
return false ;
2015-07-26 00:43:40 +02:00
}
2016-08-05 21:49:45 +02:00
if ( txMyCollateral = = CMutableTransaction ( ) ) {
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool:SendDenominate -- PrivateSend collateral not set \n " ) ;
return false ;
2014-12-09 02:17:57 +01:00
}
// lock the funds we're going to use
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CTxIn txin , txMyCollateral . vin )
vecOutPointLocked . push_back ( txin . prevout ) ;
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CTxIn txin , vecTxIn )
vecOutPointLocked . push_back ( txin . prevout ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
//BOOST_FOREACH(CTxOut o, vecTxOut)
// LogPrintf(" vecTxOut - %s\n", o.ToString());
2014-12-09 02:17:57 +01:00
2015-03-05 09:10:15 +01:00
// we should already be connected to a Masternode
2016-08-05 21:49:45 +02:00
if ( ! fSessionFoundMasternode ) {
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SendDenominate -- No Masternode has been selected yet. \n " ) ;
2014-12-09 02:17:57 +01:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2016-09-02 14:19:29 +02:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( ! CheckDiskSpace ( ) ) {
2015-05-30 05:46:29 +02:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2016-05-25 07:25:16 +02:00
fEnablePrivateSend = false ;
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SendDenominate -- Not enough disk space, disabling PrivateSend. \n " ) ;
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_ACCEPTING_ENTRIES ) ;
strLastMessage = " " ;
2014-12-09 02:17:57 +01:00
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SendDenominate -- Added transaction to pool. \n " ) ;
2014-12-09 02:17:57 +01:00
//check it against the memory pool to make sure it's valid
{
2016-03-27 20:13:08 +02:00
CValidationState validationState ;
2015-04-03 00:51:08 +02:00
CMutableTransaction tx ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxIn & txin , vecTxIn ) {
2016-09-02 14:19:29 +02:00
LogPrint ( " privatesend " , " CDarksendPool::SendDenominate -- txin=%s \n " , txin . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
tx . vin . push_back ( txin ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxOut & txout , vecTxOut ) {
2016-09-02 14:19:29 +02:00
LogPrint ( " privatesend " , " CDarksendPool::SendDenominate -- txout=%s \n " , txout . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
tx . vout . push_back ( txout ) ;
2014-12-09 02:17:57 +01:00
}
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SendDenominate -- Submitting partial tx %s " , tx . ToString ( ) ) ;
2015-03-02 00:09:33 +01:00
2016-03-27 20:13:08 +02:00
mempool . PrioritiseTransaction ( tx . GetHash ( ) , tx . GetHash ( ) . ToString ( ) , 1000 , 0.1 * COIN ) ;
TRY_LOCK ( cs_main , lockMain ) ;
2016-08-05 21:49:45 +02:00
if ( ! lockMain | | ! AcceptToMemoryPool ( mempool , validationState , CTransaction ( tx ) , false , NULL , false , true , true ) ) {
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SendDenominate -- AcceptToMemoryPool() failed! tx=%s " , tx . ToString ( ) ) ;
2016-03-27 20:13:08 +02:00
UnlockCoins ( ) ;
SetNull ( ) ;
2016-09-02 14:19:29 +02:00
return false ;
2014-12-09 02:17:57 +01:00
}
}
// store our entry for later use
2016-08-29 21:14:34 +02:00
CDarkSendEntry entry ( vecTxIn , vecTxOut , txMyCollateral ) ;
2016-08-05 21:49:45 +02:00
vecEntries . push_back ( entry ) ;
2014-12-09 02:17:57 +01:00
2016-08-29 21:14:34 +02:00
RelayIn ( entry ) ;
2016-08-05 21:49:45 +02:00
CheckPool ( ) ;
2016-09-02 14:19:29 +02:00
return true ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
// Incoming message from Masternode updating the progress of mixing
// nAcceptedEntriesCountNew:
// -1 mean's it'n not a "transaction accepted/not accepted" message, just a standard update
// 0 means transaction was not accepted
// 1 means transaction was accepted
bool CDarksendPool : : UpdatePoolStateOnClient ( int nStateNew , int nEntriesCountNew , int nAcceptedEntriesCountNew , int & nErrorID , int nSessionIDNew )
{
2014-12-09 02:17:57 +01:00
if ( fMasterNode ) return false ;
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ERROR | | nState = = POOL_STATE_SUCCESS ) return false ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
SetState ( nStateNew ) ;
nEntriesCount = nEntriesCountNew ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
strAutoDenomResult = _ ( " Masternode: " ) + " " + GetMessageByID ( nErrorID ) ;
2014-12-28 15:46:39 +01:00
2016-08-05 21:49:45 +02:00
if ( nAcceptedEntriesCountNew ! = - 1 ) {
fLastEntryAccepted = nAcceptedEntriesCountNew ;
nAcceptedEntriesCount + = nAcceptedEntriesCountNew ;
if ( nAcceptedEntriesCountNew = = 0 ) {
SetState ( POOL_STATE_ERROR ) ;
strLastMessage = GetMessageByID ( nErrorID ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( nAcceptedEntriesCountNew = = 1 & & nSessionIDNew ! = 0 ) {
nSessionID = nSessionIDNew ;
LogPrintf ( " CDarksendPool::UpdatePoolStateOnClient -- set nSessionID to %d \n " , nSessionID ) ;
fSessionFoundMasternode = true ;
2014-12-09 02:17:57 +01:00
}
}
2016-08-05 21:49:45 +02:00
if ( nStateNew = = POOL_STATE_ACCEPTING_ENTRIES ) {
if ( nAcceptedEntriesCountNew = = 1 ) {
LogPrintf ( " CDarksendPool::UpdatePoolStateOnClient -- entry accepted! \n " ) ;
fSessionFoundMasternode = true ;
2014-12-09 02:17:57 +01:00
//wait for other users. Masternode will report when ready
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_QUEUE ) ;
} else if ( nAcceptedEntriesCountNew = = 0 & & nSessionID = = 0 & & ! fSessionFoundMasternode ) {
LogPrintf ( " CDarksendPool::UpdatePoolStateOnClient -- entry not accepted by Masternode \n " ) ;
2014-12-09 02:17:57 +01:00
UnlockCoins ( ) ;
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_ACCEPTING_ENTRIES ) ;
2015-03-05 09:10:15 +01:00
DoAutomaticDenominating ( ) ; //try another Masternode
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( fSessionFoundMasternode ) return true ;
2014-12-09 02:17:57 +01:00
}
return true ;
}
2014-12-25 20:21:35 +01:00
//
2015-03-05 09:10:15 +01:00
// After we receive the finalized transaction from the Masternode, we must
2014-12-25 20:21:35 +01:00
// check it to make sure it's what we want, then sign it if we agree.
2014-12-09 02:17:57 +01:00
// If we refuse to sign, it's possible we'll be charged collateral
//
2016-08-05 21:49:45 +02:00
bool CDarksendPool : : SignFinalTransaction ( const CTransaction & finalTransactionNew , CNode * node )
{
2015-03-06 23:17:51 +01:00
if ( fMasterNode ) return false ;
2014-12-09 02:17:57 +01:00
2016-03-27 20:13:08 +02:00
finalMutableTransaction = finalTransactionNew ;
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::SignFinalTransaction -- %s " , finalMutableTransaction . ToString ( ) ) ;
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
std : : vector < CTxIn > sigs ;
2014-12-09 02:17:57 +01:00
//make sure my inputs/outputs are present, otherwise refuse to sign
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CDarkSendEntry entry , vecEntries ) {
BOOST_FOREACH ( const CTxDSIn txdsin , entry . vecTxDSIn ) {
2014-12-09 02:17:57 +01:00
/* Sign my transaction and all outputs */
2016-08-05 21:49:45 +02:00
int nMyInputIndex = - 1 ;
2014-12-09 02:17:57 +01:00
CScript prevPubKey = CScript ( ) ;
2016-08-05 21:49:45 +02:00
CTxIn txin = CTxIn ( ) ;
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
for ( unsigned int i = 0 ; i < finalMutableTransaction . vin . size ( ) ; i + + ) {
if ( finalMutableTransaction . vin [ i ] = = txdsin ) {
nMyInputIndex = i ;
prevPubKey = txdsin . prevPubKey ;
txin = txdsin ;
2014-12-09 02:17:57 +01:00
}
}
2016-08-05 21:49:45 +02:00
if ( nMyInputIndex > = 0 ) { //might have to do this one input at a time?
int nFoundOutputsCount = 0 ;
2015-04-03 00:51:08 +02:00
CAmount nValue1 = 0 ;
CAmount nValue2 = 0 ;
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
for ( unsigned int i = 0 ; i < finalMutableTransaction . vout . size ( ) ; i + + ) {
BOOST_FOREACH ( const CTxOut & txout , entry . vecTxDSOut ) {
if ( finalMutableTransaction . vout [ i ] = = txout ) {
nFoundOutputsCount + + ;
2016-03-27 20:13:08 +02:00
nValue1 + = finalMutableTransaction . vout [ i ] . nValue ;
2014-12-09 02:17:57 +01:00
}
}
}
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxOut txout , entry . vecTxDSOut )
nValue2 + = txout . nValue ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
int nTargetOuputsCount = entry . vecTxDSOut . size ( ) ;
if ( nFoundOutputsCount < nTargetOuputsCount | | nValue1 ! = nValue2 ) {
2014-12-25 20:21:35 +01:00
// in this case, something went wrong and we'll refuse to sign. It's possible we'll be charged collateral. But that's
2014-12-09 02:17:57 +01:00
// better then signing if the transaction doesn't look like what we wanted.
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::SignFinalTransaction -- My entries are not correct! Refusing to sign: nFoundOutputsCount: %d, nTargetOuputsCount: %d \n " , nFoundOutputsCount , nTargetOuputsCount ) ;
2015-05-30 05:46:29 +02:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2015-04-06 08:52:37 +02:00
2014-12-09 02:17:57 +01:00
return false ;
}
2015-04-03 00:51:08 +02:00
const CKeyStore & keystore = * pwalletMain ;
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::SignFinalTransaction -- Signing my input %i \n " , nMyInputIndex ) ;
if ( ! SignSignature ( keystore , prevPubKey , finalMutableTransaction , nMyInputIndex , int ( SIGHASH_ALL | SIGHASH_ANYONECANPAY ) ) ) { // changes scriptSig
LogPrint ( " privatesend " , " CDarksendPool::SignFinalTransaction -- Unable to sign my own transaction! \n " ) ;
2014-12-09 02:17:57 +01:00
// not sure what to do here, it will timeout...?
}
2016-08-05 21:49:45 +02:00
sigs . push_back ( finalMutableTransaction . vin [ nMyInputIndex ] ) ;
LogPrint ( " privatesend " , " CDarksendPool::SignFinalTransaction -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s \n " , nMyInputIndex , ( int ) sigs . size ( ) , ScriptToAsmStr ( finalMutableTransaction . vin [ nMyInputIndex ] . scriptSig ) ) ;
2014-12-09 02:17:57 +01:00
}
2014-12-25 20:21:35 +01:00
2014-12-09 02:17:57 +01:00
}
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::SignFinalTransaction -- finalMutableTransaction=%s " , finalMutableTransaction . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
// push all of our signatures to the Masternode
if ( sigs . size ( ) > 0 & & node ! = NULL )
node - > PushMessage ( NetMsgType : : DSSIGNFINALTX , sigs ) ;
2014-12-09 02:17:57 +01:00
return true ;
}
2015-03-02 00:09:33 +01:00
void CDarksendPool : : NewBlock ( )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
static int64_t nTimeNewBlockReceived = 0 ;
2014-12-09 02:17:57 +01:00
2015-01-27 15:46:06 +01:00
//we we're processing lots of blocks, we'll just leave
2016-08-05 21:49:45 +02:00
if ( GetTime ( ) - nTimeNewBlockReceived < 10 ) return ;
nTimeNewBlockReceived = GetTime ( ) ;
LogPrint ( " privatesend " , " CDarksendPool::NewBlock \n " ) ;
2015-01-27 15:46:06 +01:00
2016-03-04 06:58:53 +01:00
CheckTimeout ( ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
// mixing transaction was completed (failed or successful)
void CDarksendPool : : CompletedTransaction ( bool fError , int nErrorID )
2014-12-09 02:17:57 +01:00
{
if ( fMasterNode ) return ;
2016-08-05 21:49:45 +02:00
if ( fError ) {
LogPrintf ( " CompletedTransaction -- error \n " ) ;
SetState ( POOL_STATE_ERROR ) ;
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
CheckPool ( ) ;
2015-03-04 19:17:30 +01:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2014-12-09 02:17:57 +01:00
} else {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CompletedTransaction -- success \n " ) ;
SetState ( POOL_STATE_SUCCESS ) ;
2014-12-09 02:17:57 +01:00
2015-03-02 00:09:33 +01:00
UnlockCoins ( ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2014-12-09 02:17:57 +01:00
// To avoid race conditions, we'll only let DS run once per block
2016-08-05 21:49:45 +02:00
nCachedLastSuccessBlock = pCurrentBlockIndex - > nHeight ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
strLastMessage = GetMessageByID ( nErrorID ) ;
2014-12-09 02:17:57 +01:00
}
2014-12-25 20:21:35 +01:00
//
2016-08-05 21:49:45 +02:00
// Passively run mixing in the background to anonymize funds based on the given configuration.
2014-12-09 02:17:57 +01:00
//
2015-08-11 01:06:17 +02:00
bool CDarksendPool : : DoAutomaticDenominating ( bool fDryRun )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
if ( ! fEnablePrivateSend | | fMasterNode | | ! pCurrentBlockIndex ) return false ;
2016-06-15 21:13:04 +02:00
if ( ! pwalletMain | | pwalletMain - > IsLocked ( ) ) return false ;
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ERROR | | nState = = POOL_STATE_SUCCESS ) return false ;
switch ( nWalletBackups ) {
case 0 :
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- Automatic backups disabled, no mixing available. \n " ) ;
strAutoDenomResult = _ ( " Automatic backups disabled " ) + " , " + _ ( " no mixing available. " ) ;
fEnablePrivateSend = false ; // stop mixing
pwalletMain - > nKeysLeftSinceAutoBackup = 0 ; // no backup, no "keys since last backup"
return false ;
case - 1 :
// Automatic backup failed, nothing else we can do until user fixes the issue manually.
// There is no way to bring user attention in daemon mode so we just update status and
// keep spaming if debug is on.
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- ERROR! Failed to create automatic backup. \n " ) ;
strAutoDenomResult = _ ( " ERROR! Failed to create automatic backup " ) + " , " + _ ( " see debug.log for details. " ) ;
return false ;
case - 2 :
// We were able to create automatic backup but keypool was not replenished because wallet is locked.
// There is no way to bring user attention in daemon mode so we just update status and
// keep spaming if debug is on.
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- WARNING! Failed to create replenish keypool, please unlock your wallet to do so. \n " ) ;
strAutoDenomResult = _ ( " WARNING! Failed to replenish keypool, please unlock your wallet to do so. " ) + " , " + _ ( " see debug.log for details. " ) ;
return false ;
2016-06-15 21:13:04 +02:00
}
2016-08-05 21:49:45 +02:00
if ( pwalletMain - > nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP ) {
2016-06-15 21:13:04 +02:00
// We should never get here via mixing itself but probably smth else is still actively using keypool
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- Very low number of keys left: %d, no mixing available. \n " , pwalletMain - > nKeysLeftSinceAutoBackup ) ;
2016-06-15 21:13:04 +02:00
strAutoDenomResult = strprintf ( _ ( " Very low number of keys left: %d " ) + " , " + _ ( " no mixing available. " ) , pwalletMain - > nKeysLeftSinceAutoBackup ) ;
// It's getting really dangerous, stop mixing
fEnablePrivateSend = false ;
return false ;
2016-08-05 21:49:45 +02:00
} else if ( pwalletMain - > nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING ) {
2016-06-15 21:13:04 +02:00
// Low number of keys left but it's still more or less safe to continue
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- Very low number of keys left: %d \n " , pwalletMain - > nKeysLeftSinceAutoBackup ) ;
2016-06-15 21:13:04 +02:00
strAutoDenomResult = strprintf ( _ ( " Very low number of keys left: %d " ) , pwalletMain - > nKeysLeftSinceAutoBackup ) ;
2016-08-05 21:49:45 +02:00
if ( fCreateAutoBackups ) {
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- Trying to create new backup. \n " ) ;
2016-06-15 21:13:04 +02:00
std : : string warningString ;
std : : string errorString ;
if ( ! AutoBackupWallet ( pwalletMain , " " , warningString , errorString ) ) {
2016-08-05 21:49:45 +02:00
if ( ! warningString . empty ( ) ) {
2016-06-15 21:13:04 +02:00
// There were some issues saving backup but yet more or less safe to continue
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- WARNING! Something went wrong on automatic backup: %s \n " , warningString ) ;
2016-06-15 21:13:04 +02:00
}
2016-08-05 21:49:45 +02:00
if ( ! errorString . empty ( ) ) {
2016-06-15 21:13:04 +02:00
// Things are really broken
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- ERROR! Failed to create automatic backup: %s \n " , errorString ) ;
2016-06-15 21:13:04 +02:00
strAutoDenomResult = strprintf ( _ ( " ERROR! Failed to create automatic backup " ) + " : %s " , errorString ) ;
return false ;
}
}
} else {
// Wait for someone else (e.g. GUI action) to create automatic backup for us
return false ;
}
}
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- Keys left since latest backup: %d \n " , pwalletMain - > nKeysLeftSinceAutoBackup ) ;
2016-06-15 21:13:04 +02:00
2015-08-07 06:08:37 +02:00
if ( GetEntriesCount ( ) > 0 ) {
strAutoDenomResult = _ ( " Mixing in progress... " ) ;
return false ;
}
2015-08-03 21:53:00 +02:00
TRY_LOCK ( cs_darksend , lockDS ) ;
if ( ! lockDS ) {
strAutoDenomResult = _ ( " Lock is already in place. " ) ;
return false ;
}
2015-07-26 01:24:19 +02:00
if ( ! masternodeSync . IsBlockchainSynced ( ) ) {
2015-07-26 00:43:40 +02:00
strAutoDenomResult = _ ( " Can't mix while sync in progress. " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( ! fDryRun & & pwalletMain - > IsLocked ( ) ) {
2015-02-04 14:24:56 +01:00
strAutoDenomResult = _ ( " Wallet is locked. " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( ! fPrivateSendMultiSession & & pCurrentBlockIndex - > nHeight - nCachedLastSuccessBlock < nMinBlockSpacing ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Last successful PrivateSend action was too recent \n " ) ;
2016-05-09 21:08:13 +02:00
strAutoDenomResult = _ ( " Last successful PrivateSend action was too recent. " ) ;
2015-08-11 07:09:56 +02:00
return false ;
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( mnodeman . size ( ) = = 0 ) {
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- No Masternodes detected \n " ) ;
2015-03-05 09:10:15 +01:00
strAutoDenomResult = _ ( " No Masternodes detected. " ) ;
2014-12-28 02:08:45 +01:00
return false ;
}
2014-12-09 02:17:57 +01:00
// ** find the coins we'll use
2016-08-05 21:49:45 +02:00
std : : vector < CTxIn > vecTxIn ;
2015-04-30 00:06:24 +02:00
CAmount nValueMin = CENT ;
CAmount nValueIn = 0 ;
CAmount nOnlyDenominatedBalance ;
CAmount nBalanceNeedsDenominated ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CAmount nLowestDenom = vecPrivateSendDenominations . back ( ) ;
2015-11-06 18:23:19 +01:00
// if there are no confirmed DS collateral inputs yet
2016-09-01 09:03:47 +02:00
if ( ! pwalletMain - > HasCollateralInputs ( ) ) {
2015-01-21 07:09:04 +01:00
// should have some additional amount for them
2016-09-01 09:03:47 +02:00
nLowestDenom + = PRIVATESEND_COLLATERAL * 4 ;
}
2015-01-21 07:09:04 +01:00
2016-07-15 12:21:20 +02:00
CAmount nBalanceNeedsAnonymized = pwalletMain - > GetNeedsToBeAnonymizedBalance ( nLowestDenom ) ;
2014-12-09 02:17:57 +01:00
2015-11-06 18:23:19 +01:00
// anonymizable balance is way too small
2016-08-05 21:49:45 +02:00
if ( nBalanceNeedsAnonymized < nLowestDenom ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Not enough funds to anonymize \n " ) ;
2016-07-15 12:21:20 +02:00
strAutoDenomResult = _ ( " Not enough funds to anonymize. " ) ;
2015-03-02 00:09:33 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- nLowestDenom: %f, nBalanceNeedsAnonymized: %f \n " , ( float ) nLowestDenom / COIN , ( float ) nBalanceNeedsAnonymized / COIN ) ;
2015-01-25 00:57:55 +01:00
2014-12-09 02:17:57 +01:00
// select coins that should be given to the pool
2016-08-05 21:49:45 +02:00
if ( ! pwalletMain - > SelectCoinsDark ( nValueMin , nBalanceNeedsAnonymized , vecTxIn , nValueIn , 0 , nPrivateSendRounds ) )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
if ( pwalletMain - > SelectCoinsDark ( nValueMin , 9999999 * COIN , vecTxIn , nValueIn , - 2 , 0 ) )
2014-12-09 02:17:57 +01:00
{
2015-07-26 01:24:19 +02:00
nOnlyDenominatedBalance = pwalletMain - > GetDenominatedBalance ( true ) + pwalletMain - > GetDenominatedBalance ( ) - pwalletMain - > GetAnonymizedBalance ( ) ;
2015-04-30 00:06:24 +02:00
nBalanceNeedsDenominated = nBalanceNeedsAnonymized - nOnlyDenominatedBalance ;
if ( nBalanceNeedsDenominated > nValueIn ) nBalanceNeedsDenominated = nValueIn ;
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- `SelectCoinsDark` (%f - (%f + %f - %f = %f) ) = %f \n " ,
2016-05-30 08:22:30 +02:00
( float ) nBalanceNeedsAnonymized / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( true ) / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( ) / COIN ,
( float ) pwalletMain - > GetAnonymizedBalance ( ) / COIN ,
( float ) nOnlyDenominatedBalance / COIN ,
( float ) nBalanceNeedsDenominated / COIN ) ;
2016-07-15 12:21:20 +02:00
if ( nBalanceNeedsDenominated < nLowestDenom ) { // most likely we are just waiting for denoms to confirm
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- No funds detected in need of denominating \n " ) ;
2016-07-15 12:21:20 +02:00
strAutoDenomResult = _ ( " No funds detected in need of denominating. " ) ;
return false ;
}
if ( ! fDryRun ) return CreateDenominated ( ) ;
2015-04-30 00:06:24 +02:00
2014-12-09 02:17:57 +01:00
return true ;
2015-01-15 15:41:56 +01:00
} else {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Can't denominate (no compatible inputs left) \n " ) ;
2015-02-04 14:24:56 +01:00
strAutoDenomResult = _ ( " Can't denominate: no compatible inputs left. " ) ;
2015-01-15 15:41:56 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
}
2015-06-24 18:30:05 +02:00
if ( fDryRun ) return true ;
2015-07-26 01:24:19 +02:00
nOnlyDenominatedBalance = pwalletMain - > GetDenominatedBalance ( true ) + pwalletMain - > GetDenominatedBalance ( ) - pwalletMain - > GetAnonymizedBalance ( ) ;
2015-04-30 00:06:24 +02:00
nBalanceNeedsDenominated = nBalanceNeedsAnonymized - nOnlyDenominatedBalance ;
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- 'nBalanceNeedsDenominated > 0' (%f - (%f + %f - %f = %f) ) = %f \n " ,
2016-05-30 08:22:30 +02:00
( float ) nBalanceNeedsAnonymized / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( true ) / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( ) / COIN ,
( float ) pwalletMain - > GetAnonymizedBalance ( ) / COIN ,
( float ) nOnlyDenominatedBalance / COIN ,
( float ) nBalanceNeedsDenominated / COIN ) ;
2015-04-30 00:06:24 +02:00
2015-06-24 18:30:05 +02:00
//check if we have should create more denominated inputs
2016-07-15 12:21:20 +02:00
if ( nBalanceNeedsDenominated > 0 ) return CreateDenominated ( ) ;
2014-12-09 02:17:57 +01:00
2015-06-24 18:30:05 +02:00
//check if we have the collateral sized inputs
2016-08-05 21:49:45 +02:00
if ( ! pwalletMain - > HasCollateralInputs ( ) )
return ! pwalletMain - > HasCollateralInputs ( false ) & & MakeCollateralAmounts ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( fSessionFoundMasternode ) {
strAutoDenomResult = _ ( " Mixing in progress... " ) ;
return false ;
}
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
// Initial phase, find a Masternode
// Clean if there is anything left from previous session
UnlockCoins ( ) ;
SetNull ( ) ;
2015-05-30 05:46:29 +02:00
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_ACCEPTING_ENTRIES ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( ! fPrivateSendMultiSession & & pwalletMain - > GetDenominatedBalance ( true ) > 0 ) { //get denominated unconfirmed inputs
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Found unconfirmed denominated outputs, will wait till they confirm to continue. \n " ) ;
strAutoDenomResult = _ ( " Found unconfirmed denominated outputs, will wait till they confirm to continue. " ) ;
return false ;
}
//check our collateral and create new if needed
std : : string strReason ;
if ( txMyCollateral = = CMutableTransaction ( ) ) {
if ( ! pwalletMain - > CreateCollateralTransaction ( txMyCollateral , strReason ) ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- create collateral error:%s \n " , strReason ) ;
2015-08-13 00:54:21 +02:00
return false ;
}
2016-08-05 21:49:45 +02:00
} else {
if ( ! IsCollateralValid ( txMyCollateral ) ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- invalid collateral, recreating... \n " ) ;
if ( ! pwalletMain - > CreateCollateralTransaction ( txMyCollateral , strReason ) ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- create collateral error: %s \n " , strReason ) ;
2015-05-30 05:46:29 +02:00
return false ;
}
2015-04-07 18:41:31 +02:00
}
2016-08-05 21:49:45 +02:00
}
2015-04-07 18:41:31 +02:00
2016-08-05 21:49:45 +02:00
//if we've used 90% of the Masternode list then drop all the oldest first
int nThreshold = ( int ) ( mnodeman . CountEnabled ( MIN_PRIVATESEND_PEER_PROTO_VERSION ) * 0.9 ) ;
LogPrint ( " privatesend " , " Checking vecMasternodesUsed: size: %d, threshold: %d \n " , ( int ) vecMasternodesUsed . size ( ) , nThreshold ) ;
while ( ( int ) vecMasternodesUsed . size ( ) > nThreshold ) {
vecMasternodesUsed . erase ( vecMasternodesUsed . begin ( ) ) ;
LogPrint ( " privatesend " , " vecMasternodesUsed: size: %d, threshold: %d \n " , ( int ) vecMasternodesUsed . size ( ) , nThreshold ) ;
}
2014-12-30 01:09:34 +01:00
2016-08-05 21:49:45 +02:00
bool fUseQueue = insecure_rand ( ) % 100 > 33 ;
// don't use the queues all of the time for mixing unless we are a liquidity provider
if ( nLiquidityProvider | | fUseQueue ) {
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
// Look through the queues and see if anything matches
BOOST_FOREACH ( CDarksendQueue & dsq , vecDarksendQueue ) {
if ( dsq . nTime = = 0 ) continue ;
if ( dsq . IsExpired ( ) ) continue ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CService addr ;
if ( ! dsq . GetAddress ( addr ) ) continue ;
2015-01-19 22:25:03 +01:00
2016-08-05 21:49:45 +02:00
int nProtocolVersion ;
if ( ! dsq . GetProtocolVersion ( nProtocolVersion ) ) continue ;
if ( nProtocolVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) continue ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
// incompatible denom
if ( dsq . nDenom > = ( 1 < < vecPrivateSendDenominations . size ( ) ) ) continue ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
bool fUsed = false ;
//don't reuse Masternodes
BOOST_FOREACH ( CTxIn txinUsed , vecMasternodesUsed ) {
if ( dsq . vin = = txinUsed ) {
fUsed = true ;
break ;
2014-12-09 02:17:57 +01:00
}
}
2016-08-05 21:49:45 +02:00
if ( fUsed ) continue ;
2014-12-09 02:17:57 +01:00
2016-08-29 21:17:00 +02:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- found valid queue: %s \n " , dsq . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
std : : vector < CTxIn > vecTxInTmp ;
std : : vector < COutput > vCoinsTmp ;
// Try to match their denominations if possible
if ( ! pwalletMain - > SelectCoinsByDenominations ( dsq . nDenom , nValueMin , nBalanceNeedsAnonymized , vecTxInTmp , vCoinsTmp , nValueIn , 0 , nPrivateSendRounds ) ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Couldn't match denominations %d \n " , dsq . nDenom ) ;
continue ;
2015-02-26 02:55:27 +01:00
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CMasternode * pmn = mnodeman . Find ( dsq . vin ) ;
if ( pmn = = NULL ) {
2016-08-12 07:43:18 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- dsq masternode is not in masternode list! vin=%s \n " , dsq . vin . ToString ( ) ) ;
2015-01-20 16:59:37 +01:00
continue ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- attempt to connect to masternode from queue, addr=%s \n " , pmn - > addr . ToString ( ) ) ;
nLastTimeChanged = GetTimeMillis ( ) ;
// connect to Masternode and submit the queue request
CNode * pnode = ConnectNode ( ( CAddress ) addr , NULL , true ) ;
if ( pnode ! = NULL ) {
2015-08-04 22:51:16 +02:00
pSubmittedToMasternode = pmn ;
2016-08-05 21:49:45 +02:00
vecMasternodesUsed . push_back ( dsq . vin ) ;
nSessionDenom = dsq . nDenom ;
2015-08-04 22:51:16 +02:00
2016-08-05 21:49:45 +02:00
pnode - > PushMessage ( NetMsgType : : DSACCEPT , nSessionDenom , txMyCollateral ) ;
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- connected (from queue), sending dsa: nSessionDenom: %d, addr=%s \n " , nSessionDenom , pnode - > addr . ToString ( ) ) ;
2015-08-04 22:51:16 +02:00
strAutoDenomResult = _ ( " Mixing in progress... " ) ;
2016-08-05 21:49:45 +02:00
dsq . nTime = 0 ; //remove node
2015-08-04 22:51:16 +02:00
return true ;
2014-12-09 02:17:57 +01:00
} else {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- error connecting \n " ) ;
strAutoDenomResult = _ ( " Error connecting to Masternode. " ) ;
dsq . nTime = 0 ; //remove node
2015-01-20 16:59:37 +01:00
continue ;
2014-12-09 02:17:57 +01:00
}
}
2016-08-05 21:49:45 +02:00
}
2015-01-20 16:59:37 +01:00
2016-08-05 21:49:45 +02:00
// do not initiate queue if we are a liquidity provider to avoid useless inter-mixing
if ( nLiquidityProvider ) return false ;
int nTries = 0 ;
// otherwise, try one randomly
while ( nTries < 10 ) {
CMasternode * pmn = mnodeman . FindRandomNotInVec ( vecMasternodesUsed , MIN_PRIVATESEND_PEER_PROTO_VERSION ) ;
if ( pmn = = NULL ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Can't find random masternode! \n " ) ;
strAutoDenomResult = _ ( " Can't find random Masternode. " ) ;
return false ;
}
if ( pmn - > nLastDsq ! = 0 & & pmn - > nLastDsq + mnodeman . CountEnabled ( MIN_PRIVATESEND_PEER_PROTO_VERSION ) / 5 > mnodeman . nDsqCount ) {
nTries + + ;
continue ;
}
nLastTimeChanged = GetTimeMillis ( ) ;
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- attempt %d connection to Masternode %s \n " , nTries , pmn - > addr . ToString ( ) ) ;
CNode * pnode = ConnectNode ( ( CAddress ) pmn - > addr , NULL , true ) ;
if ( pnode ! = NULL ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- connected %s \n " , pmn - > vin . ToString ( ) ) ;
pSubmittedToMasternode = pmn ;
vecMasternodesUsed . push_back ( pmn - > vin ) ;
std : : vector < CAmount > vecAmounts ;
pwalletMain - > ConvertList ( vecTxIn , vecAmounts ) ;
// try to get a single random denom out of vecAmounts
2016-09-05 18:09:25 +02:00
while ( nSessionDenom = = 0 ) {
2016-08-05 21:49:45 +02:00
nSessionDenom = GetDenominationsByAmounts ( vecAmounts ) ;
2016-09-05 18:09:25 +02:00
}
2016-08-05 21:49:45 +02:00
pnode - > PushMessage ( NetMsgType : : DSACCEPT , nSessionDenom , txMyCollateral ) ;
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- connected, sending DSACCEPT, nSessionDenom: %d \n " , nSessionDenom ) ;
strAutoDenomResult = _ ( " Mixing in progress... " ) ;
return true ;
} else {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- can't connect %s \n " , pmn - > vin . ToString ( ) ) ;
vecMasternodesUsed . push_back ( pmn - > vin ) ; // postpone MN we wasn't able to connect to
nTries + + ;
continue ;
}
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
strAutoDenomResult = _ ( " No compatible Masternode found. " ) ;
2015-01-06 22:05:39 +01:00
return false ;
2014-12-30 01:09:34 +01:00
}
2014-12-09 02:17:57 +01:00
2016-09-02 14:19:29 +02:00
bool CDarksendPool : : SubmitDenominate ( )
2014-12-30 01:09:34 +01:00
{
2016-08-05 21:49:45 +02:00
std : : string strError ;
2016-09-02 14:19:29 +02:00
std : : vector < CTxIn > vecTxInRet ;
std : : vector < CTxOut > vecTxOutRet ;
2016-08-05 21:49:45 +02:00
2015-09-09 07:00:30 +02:00
// Submit transaction to the pool if we get here
// Try to use only inputs with the same number of rounds starting from lowest number of rounds possible
2016-05-25 07:25:16 +02:00
for ( int i = 0 ; i < nPrivateSendRounds ; i + + ) {
2016-09-02 14:19:29 +02:00
if ( PrepareDenominate ( i , i + 1 , strError , vecTxInRet , vecTxOutRet ) ) {
LogPrintf ( " CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success \n " , i ) ;
return SendDenominate ( vecTxInRet , vecTxOutRet ) ;
2016-07-15 12:21:20 +02:00
}
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s \n " , i , strError ) ;
2015-09-09 07:00:30 +02:00
}
2014-12-25 20:21:35 +01:00
2015-09-09 07:00:30 +02:00
// We failed? That's strange but let's just make final attempt and try to mix everything
2016-09-02 14:19:29 +02:00
if ( PrepareDenominate ( 0 , nPrivateSendRounds , strError , vecTxInRet , vecTxOutRet ) ) {
LogPrintf ( " CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for all rounds, success \n " ) ;
return SendDenominate ( vecTxInRet , vecTxOutRet ) ;
2016-08-05 21:49:45 +02:00
}
2014-12-09 02:17:57 +01:00
2015-09-09 07:00:30 +02:00
// Should never actually get here but just in case
2016-09-02 14:19:29 +02:00
LogPrintf ( " CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for all rounds, error: %s \n " , strError ) ;
2014-12-30 01:09:34 +01:00
strAutoDenomResult = strError ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-09-02 14:19:29 +02:00
bool CDarksendPool : : PrepareDenominate ( int nMinRounds , int nMaxRounds , std : : string & strErrorRet , std : : vector < CTxIn > & vecTxInRet , std : : vector < CTxOut > & vecTxOutRet )
{
if ( pwalletMain - > IsLocked ( ) ) {
strErrorRet = " Wallet locked, unable to create transaction! " ;
return false ;
}
if ( GetState ( ) ! = POOL_STATE_ERROR & & GetState ( ) ! = POOL_STATE_SUCCESS & & GetEntriesCount ( ) > 0 ) {
strErrorRet = " You already have pending entries in the PrivateSend pool " ;
return false ;
}
// make sure returning vectors are empty before filling them up
vecTxInRet . clear ( ) ;
vecTxOutRet . clear ( ) ;
// ** find the coins we'll use
std : : vector < CTxIn > vecTxIn ;
std : : vector < COutput > vCoins ;
CAmount nValueIn = 0 ;
CReserveKey reservekey ( pwalletMain ) ;
/*
Select the coins we ' ll use
if nMinRounds > = 0 it means only denominated inputs are going in and coming out
*/
bool fSelected = pwalletMain - > SelectCoinsByDenominations ( nSessionDenom , 0.1 * COIN , PRIVATESEND_POOL_MAX , vecTxIn , vCoins , nValueIn , nMinRounds , nMaxRounds ) ;
if ( nMinRounds > = 0 & & ! fSelected ) {
strErrorRet = " Can't select current denominated inputs " ;
return false ;
}
LogPrintf ( " CDarksendPool::PrepareDenominate -- max value: %f \n " , ( double ) nValueIn / COIN ) ;
{
LOCK ( pwalletMain - > cs_wallet ) ;
BOOST_FOREACH ( CTxIn txin , vecTxIn ) {
pwalletMain - > LockCoin ( txin . prevout ) ;
}
}
CAmount nValueLeft = nValueIn ;
/*
TODO : Front load with needed denominations ( e . g . .1 , 1 )
*/
// Make outputs by looping through denominations: try to add every needed denomination, repeat up to 5-10 times.
// This way we can be pretty sure that it should have at least one of each needed denomination.
// NOTE: No need to randomize order of inputs because they were
// initially shuffled in CWallet::SelectCoinsByDenominations already.
int nStep = 0 ;
int nStepsMax = 5 + GetRandInt ( 5 ) ;
while ( nStep < nStepsMax ) {
BOOST_FOREACH ( CAmount nValueDenom , vecPrivateSendDenominations ) {
// only use the ones that are approved
if ( ! ( ( nSessionDenom & ( 1 < < 0 ) ) & & nValueDenom = = 100 * COIN + 100000 ) & &
! ( ( nSessionDenom & ( 1 < < 1 ) ) & & nValueDenom = = 10 * COIN + 10000 ) & &
! ( ( nSessionDenom & ( 1 < < 2 ) ) & & nValueDenom = = 1 * COIN + 1000 ) & &
! ( ( nSessionDenom & ( 1 < < 3 ) ) & & nValueDenom = = .1 * COIN + 100 ) )
{ continue ; }
// try to add it
if ( nValueLeft - nValueDenom > = 0 ) {
// Note: this relies on a fact that both vectors MUST have same size
std : : vector < CTxIn > : : iterator it = vecTxIn . begin ( ) ;
std : : vector < COutput > : : iterator it2 = vCoins . begin ( ) ;
while ( it2 ! = vCoins . end ( ) ) {
// we have matching inputs
if ( ( * it2 ) . tx - > vout [ ( * it2 ) . i ] . nValue = = nValueDenom ) {
// add new input in resulting vector
vecTxInRet . push_back ( * it ) ;
// remove corresponting items from initial vectors
vecTxIn . erase ( it ) ;
vCoins . erase ( it2 ) ;
CScript scriptChange ;
CPubKey vchPubKey ;
// use a unique change address
assert ( reservekey . GetReservedKey ( vchPubKey ) ) ; // should never fail, as we just unlocked
scriptChange = GetScriptForDestination ( vchPubKey . GetID ( ) ) ;
reservekey . KeepKey ( ) ;
// add new output
CTxOut txout ( nValueDenom , scriptChange ) ;
vecTxOutRet . push_back ( txout ) ;
// subtract denomination amount
nValueLeft - = nValueDenom ;
break ;
}
+ + it ;
+ + it2 ;
}
}
}
nStep + + ;
if ( nValueLeft = = 0 ) break ;
}
{
// unlock unused coins
LOCK ( pwalletMain - > cs_wallet ) ;
BOOST_FOREACH ( CTxIn txin , vecTxIn ) {
pwalletMain - > UnlockCoin ( txin . prevout ) ;
}
}
if ( GetDenominations ( vecTxOutRet ) ! = nSessionDenom ) {
// unlock used coins on failure
LOCK ( pwalletMain - > cs_wallet ) ;
BOOST_FOREACH ( CTxIn txin , vecTxInRet ) {
pwalletMain - > UnlockCoin ( txin . prevout ) ;
}
strErrorRet = " Can't make current denominated outputs " ;
return false ;
}
// We also do not care about full amount as long as we have right denominations
return true ;
}
2016-07-15 12:21:20 +02:00
// Create collaterals by looping through inputs grouped by addresses
2015-03-02 00:09:33 +01:00
bool CDarksendPool : : MakeCollateralAmounts ( )
2016-07-15 12:21:20 +02:00
{
std : : vector < CompactTallyItem > vecTally ;
2016-07-29 07:27:05 +02:00
if ( ! pwalletMain - > SelectCoinsGrouppedByAddresses ( vecTally , false ) ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::MakeCollateralAmounts -- SelectCoinsGrouppedByAddresses can't find any inputs! \n " ) ;
2016-07-15 12:21:20 +02:00
return false ;
}
BOOST_FOREACH ( CompactTallyItem & item , vecTally ) {
if ( ! MakeCollateralAmounts ( item ) ) continue ;
return true ;
}
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::MakeCollateralAmounts -- failed! \n " ) ;
2016-07-15 12:21:20 +02:00
return false ;
}
// Split up large inputs or create fee sized inputs
bool CDarksendPool : : MakeCollateralAmounts ( const CompactTallyItem & tallyItem )
2014-12-09 02:17:57 +01:00
{
CWalletTx wtx ;
2016-01-24 05:21:14 +01:00
CAmount nFeeRet = 0 ;
2016-02-02 16:28:56 +01:00
int nChangePosRet = - 1 ;
2014-12-09 02:17:57 +01:00
std : : string strFail = " " ;
2016-07-15 12:21:20 +02:00
std : : vector < CRecipient > vecSend ;
2014-12-09 02:17:57 +01:00
2015-06-22 19:59:27 +02:00
// make our collateral address
CReserveKey reservekeyCollateral ( pwalletMain ) ;
// make our change address
CReserveKey reservekeyChange ( pwalletMain ) ;
CScript scriptCollateral ;
CPubKey vchPubKey ;
assert ( reservekeyCollateral . GetReservedKey ( vchPubKey ) ) ; // should never fail, as we just unlocked
scriptCollateral = GetScriptForDestination ( vchPubKey . GetID ( ) ) ;
2016-09-01 09:03:47 +02:00
vecSend . push_back ( ( CRecipient ) { scriptCollateral , PRIVATESEND_COLLATERAL * 4 , false } ) ;
2014-12-09 02:17:57 +01:00
2016-07-15 12:21:20 +02:00
// try to use non-denominated and not mn-like funds first, select them explicitly
CCoinControl coinControl ;
coinControl . fAllowOtherInputs = false ;
coinControl . fAllowWatchOnly = false ;
// send change to the same address so that we were able create more denoms out of it later
coinControl . destChange = tallyItem . address . Get ( ) ;
BOOST_FOREACH ( const CTxIn & txin , tallyItem . vecTxIn )
coinControl . Select ( txin . prevout ) ;
bool fSuccess = pwalletMain - > CreateTransaction ( vecSend , wtx , reservekeyChange ,
nFeeRet , nChangePosRet , strFail , & coinControl , true , ONLY_NONDENOMINATED_NOT1000IFMN ) ;
if ( ! fSuccess ) {
2015-08-16 02:26:20 +02:00
// if we failed (most likeky not enough funds), try to use all coins instead -
2015-01-27 05:39:27 +01:00
// MN-like funds should not be touched in any case and we can't mix denominated without collaterals anyway
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::MakeCollateralAmounts -- ONLY_NONDENOMINATED_NOT1000IFMN Error: %s \n " , strFail ) ;
2016-07-15 12:21:20 +02:00
CCoinControl * coinControlNull = NULL ;
fSuccess = pwalletMain - > CreateTransaction ( vecSend , wtx , reservekeyChange ,
nFeeRet , nChangePosRet , strFail , coinControlNull , true , ONLY_NOT1000IFMN ) ;
if ( ! fSuccess ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::MakeCollateralAmounts -- ONLY_NOT1000IFMN Error: %s \n " , strFail ) ;
2015-06-22 19:59:27 +02:00
reservekeyCollateral . ReturnKey ( ) ;
2015-01-27 05:39:27 +01:00
return false ;
}
2014-12-09 02:17:57 +01:00
}
2015-06-22 19:59:27 +02:00
reservekeyCollateral . KeepKey ( ) ;
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::MakeCollateralAmounts -- txid=%s \n " , wtx . GetHash ( ) . GetHex ( ) ) ;
2015-06-24 18:30:05 +02:00
2016-08-05 21:49:45 +02:00
// use the same nCachedLastSuccessBlock as for DS mixinx to prevent race
2015-06-24 18:30:05 +02:00
if ( ! pwalletMain - > CommitTransaction ( wtx , reservekeyChange ) ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::MakeCollateralAmounts -- CommitTransaction failed! \n " ) ;
2015-06-24 18:30:05 +02:00
return false ;
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
nCachedLastSuccessBlock = pCurrentBlockIndex - > nHeight ;
2014-12-09 02:17:57 +01:00
return true ;
}
2016-07-15 12:21:20 +02:00
// Create denominations by looping through inputs grouped by addresses
bool CDarksendPool : : CreateDenominated ( )
{
std : : vector < CompactTallyItem > vecTally ;
if ( ! pwalletMain - > SelectCoinsGrouppedByAddresses ( vecTally ) ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::CreateDenominated -- SelectCoinsGrouppedByAddresses can't find any inputs! \n " ) ;
2016-07-15 12:21:20 +02:00
return false ;
}
BOOST_FOREACH ( CompactTallyItem & item , vecTally ) {
if ( ! CreateDenominated ( item ) ) continue ;
return true ;
}
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::CreateDenominated -- failed! \n " ) ;
2016-07-15 12:21:20 +02:00
return false ;
}
2015-01-15 15:41:56 +01:00
// Create denominations
2016-07-15 12:21:20 +02:00
bool CDarksendPool : : CreateDenominated ( const CompactTallyItem & tallyItem )
2015-01-15 15:41:56 +01:00
{
2016-07-15 12:21:20 +02:00
std : : vector < CRecipient > vecSend ;
CAmount nValueLeft = tallyItem . nAmount ;
2016-09-01 09:03:47 +02:00
nValueLeft - = PRIVATESEND_COLLATERAL ; // leave some room for fees
2015-01-15 15:41:56 +01:00
2016-08-05 21:49:45 +02:00
LogPrintf ( " CreateDenominated0 nValueLeft: %f \n " , ( float ) nValueLeft / COIN ) ;
2015-06-22 19:59:27 +02:00
// make our collateral address
CReserveKey reservekeyCollateral ( pwalletMain ) ;
CScript scriptCollateral ;
CPubKey vchPubKey ;
assert ( reservekeyCollateral . GetReservedKey ( vchPubKey ) ) ; // should never fail, as we just unlocked
scriptCollateral = GetScriptForDestination ( vchPubKey . GetID ( ) ) ;
2015-01-27 05:13:34 +01:00
// ****** Add collateral outputs ************ /
2016-07-15 12:21:20 +02:00
2015-01-27 05:13:34 +01:00
if ( ! pwalletMain - > HasCollateralInputs ( ) ) {
2016-09-01 09:03:47 +02:00
vecSend . push_back ( ( CRecipient ) { scriptCollateral , PRIVATESEND_COLLATERAL * 4 , false } ) ;
nValueLeft - = PRIVATESEND_COLLATERAL * 4 ;
2015-01-21 07:09:04 +01:00
}
2015-01-15 15:41:56 +01:00
// ****** Add denoms ************ /
2015-12-02 23:12:16 +01:00
2016-07-15 12:21:20 +02:00
// make our denom addresses
CReserveKey reservekeyDenom ( pwalletMain ) ;
2016-03-27 20:13:08 +02:00
// try few times - skipping smallest denoms first if there are too much already, if failed - use them
int nOutputsTotal = 0 ;
bool fSkip = true ;
do {
2015-12-02 23:12:16 +01:00
2016-08-05 21:49:45 +02:00
BOOST_REVERSE_FOREACH ( CAmount nDenomValue , vecPrivateSendDenominations ) {
2015-12-02 23:12:16 +01:00
2016-03-27 20:13:08 +02:00
if ( fSkip ) {
// Note: denoms are skipped if there are already DENOMS_COUNT_MAX of them
// and there are still larger denoms which can be used for mixing
2015-12-02 23:12:16 +01:00
2016-03-27 20:13:08 +02:00
// check skipped denoms
2016-08-05 21:49:45 +02:00
if ( IsDenomSkipped ( nDenomValue ) ) continue ;
2015-12-02 23:12:16 +01:00
2016-03-27 20:13:08 +02:00
// find new denoms to skip if any (ignore the largest one)
2016-08-05 21:49:45 +02:00
if ( nDenomValue ! = vecPrivateSendDenominations [ 0 ] & & pwalletMain - > CountInputsWithAmount ( nDenomValue ) > DENOMS_COUNT_MAX ) {
strAutoDenomResult = strprintf ( _ ( " Too many %f denominations, removing. " ) , ( float ) nDenomValue / COIN ) ;
LogPrintf ( " CDarksendPool::CreateDenominated -- %s \n " , strAutoDenomResult ) ;
vecDenominationsSkipped . push_back ( nDenomValue ) ;
2016-03-27 20:13:08 +02:00
continue ;
}
}
2015-01-15 15:41:56 +01:00
2016-03-27 20:13:08 +02:00
int nOutputs = 0 ;
2015-01-15 15:41:56 +01:00
2016-03-27 20:13:08 +02:00
// add each output up to 10 times until it can't be added again
2016-08-05 21:49:45 +02:00
while ( nValueLeft - nDenomValue > = 0 & & nOutputs < = 10 ) {
2016-03-27 20:13:08 +02:00
CScript scriptDenom ;
CPubKey vchPubKey ;
//use a unique change address
assert ( reservekeyDenom . GetReservedKey ( vchPubKey ) ) ; // should never fail, as we just unlocked
scriptDenom = GetScriptForDestination ( vchPubKey . GetID ( ) ) ;
// TODO: do not keep reservekeyDenom here
reservekeyDenom . KeepKey ( ) ;
2015-01-15 15:41:56 +01:00
2016-08-05 21:49:45 +02:00
vecSend . push_back ( ( CRecipient ) { scriptDenom , nDenomValue , false } ) ;
2015-01-15 15:41:56 +01:00
2016-03-27 20:13:08 +02:00
//increment outputs and subtract denomination amount
nOutputs + + ;
2016-08-05 21:49:45 +02:00
nValueLeft - = nDenomValue ;
LogPrintf ( " CreateDenominated1: nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f \n " , nOutputsTotal , nOutputs , ( float ) nValueLeft / COIN ) ;
2016-03-27 20:13:08 +02:00
}
nOutputsTotal + = nOutputs ;
if ( nValueLeft = = 0 ) break ;
}
2016-08-05 21:49:45 +02:00
LogPrintf ( " CreateDenominated2: nOutputsTotal: %d, nValueLeft: %f \n " , nOutputsTotal , ( float ) nValueLeft / COIN ) ;
2016-03-27 20:13:08 +02:00
// if there were no outputs added, start over without skipping
fSkip = ! fSkip ;
} while ( nOutputsTotal = = 0 & & ! fSkip ) ;
2016-08-05 21:49:45 +02:00
LogPrintf ( " CreateDenominated3: nOutputsTotal: %d, nValueLeft: %f \n " , nOutputsTotal , ( float ) nValueLeft / COIN ) ;
2015-01-15 15:41:56 +01:00
2015-01-21 07:09:04 +01:00
// if we have anything left over, it will be automatically send back as change - there is no need to send it manually
2015-01-15 15:41:56 +01:00
2016-07-15 12:21:20 +02:00
CCoinControl coinControl ;
coinControl . fAllowOtherInputs = false ;
coinControl . fAllowWatchOnly = false ;
// send change to the same address so that we were able create more denoms out of it later
coinControl . destChange = tallyItem . address . Get ( ) ;
BOOST_FOREACH ( const CTxIn & txin , tallyItem . vecTxIn )
coinControl . Select ( txin . prevout ) ;
CWalletTx wtx ;
CAmount nFeeRet = 0 ;
int nChangePosRet = - 1 ;
std : : string strFail = " " ;
// make our change address
CReserveKey reservekeyChange ( pwalletMain ) ;
bool fSuccess = pwalletMain - > CreateTransaction ( vecSend , wtx , reservekeyChange ,
nFeeRet , nChangePosRet , strFail , & coinControl , true , ONLY_NONDENOMINATED_NOT1000IFMN ) ;
if ( ! fSuccess ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::CreateDenominated -- Error: %s \n " , strFail ) ;
2015-06-22 19:59:27 +02:00
// TODO: return reservekeyDenom here
reservekeyCollateral . ReturnKey ( ) ;
2015-01-15 15:41:56 +01:00
return false ;
}
2015-06-22 19:59:27 +02:00
// TODO: keep reservekeyDenom here
reservekeyCollateral . KeepKey ( ) ;
2016-07-15 12:21:20 +02:00
if ( ! pwalletMain - > CommitTransaction ( wtx , reservekeyChange ) ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::CreateDenominated -- CommitTransaction failed! \n " ) ;
2016-07-15 12:21:20 +02:00
return false ;
}
2015-01-15 15:41:56 +01:00
2016-08-05 21:49:45 +02:00
// use the same nCachedLastSuccessBlock as for DS mixing to prevent race
nCachedLastSuccessBlock = pCurrentBlockIndex - > nHeight ;
LogPrintf ( " CDarksendPool::CreateDenominated -- txid=%s \n " , wtx . GetHash ( ) . GetHex ( ) ) ;
2015-01-15 15:41:56 +01:00
return true ;
}
2016-08-29 21:14:34 +02:00
bool CDarksendPool : : IsOutputsCompatibleWithSessionDenom ( const std : : vector < CTxDSOut > & vecTxDSOut )
2014-12-09 02:17:57 +01:00
{
2016-08-29 21:14:34 +02:00
if ( GetDenominations ( vecTxDSOut ) = = 0 ) return false ;
2015-04-06 17:39:48 +02:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CDarkSendEntry entry , vecEntries ) {
2016-08-29 21:14:34 +02:00
LogPrintf ( " CDarksendPool::IsOutputsCompatibleWithSessionDenom -- vecTxDSOut denom %d, entry.vecTxDSOut denom %d \n " , GetDenominations ( vecTxDSOut ) , GetDenominations ( entry . vecTxDSOut ) ) ;
if ( GetDenominations ( vecTxDSOut ) ! = GetDenominations ( entry . vecTxDSOut ) ) return false ;
2014-12-09 02:17:57 +01:00
}
return true ;
}
2016-08-05 21:49:45 +02:00
bool CDarksendPool : : IsDenomCompatibleWithSession ( int nDenom , CTransaction txCollateral , int & nErrorID )
2014-12-09 02:17:57 +01:00
{
2015-04-07 01:07:25 +02:00
if ( nDenom = = 0 ) return false ;
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::IsDenomCompatibleWithSession -- nSessionDenom: %d nSessionUsers: %d \n " , nSessionDenom , nSessionUsers ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( ! fUnitTest & & ! IsCollateralValid ( txCollateral ) ) {
LogPrint ( " privatesend " , " CDarksendPool::IsDenomCompatibleWithSession -- collateral not valid! \n " ) ;
nErrorID = ERR_INVALID_COLLATERAL ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( nSessionUsers < 0 ) nSessionUsers = 0 ;
2014-12-25 20:21:35 +01:00
2016-08-05 21:49:45 +02:00
if ( nSessionUsers = = 0 ) {
nSessionID = 1 + GetRand ( 999999 ) ;
nSessionDenom = nDenom ;
nSessionUsers + + ;
nLastTimeChanged = GetTimeMillis ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( ! fUnitTest ) {
2015-03-02 00:09:33 +01:00
//broadcast that I'm accepting entries, only if it's the first entry through
2016-08-05 21:49:45 +02:00
CDarksendQueue dsq ( nDenom , activeMasternode . vin , GetTime ( ) , false ) ;
2016-08-29 21:17:00 +02:00
LogPrint ( " privatesend " , " CDarksendPool::IsDenomCompatibleWithSession -- signing and relaying new queue: %s \n " , dsq . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
dsq . Sign ( ) ;
dsq . Relay ( ) ;
}
2016-08-05 21:49:45 +02:00
SetState ( POOL_STATE_QUEUE ) ;
2014-12-09 02:17:57 +01:00
vecSessionCollateral . push_back ( txCollateral ) ;
return true ;
}
2016-08-05 21:49:45 +02:00
if ( ( nState ! = POOL_STATE_ACCEPTING_ENTRIES & & nState ! = POOL_STATE_QUEUE ) | | nSessionUsers > = GetMaxPoolTransactions ( ) ) {
if ( ( nState ! = POOL_STATE_ACCEPTING_ENTRIES & & nState ! = POOL_STATE_QUEUE ) ) nErrorID = ERR_MODE ;
if ( nSessionUsers > = GetMaxPoolTransactions ( ) ) nErrorID = ERR_QUEUE_FULL ;
LogPrintf ( " CDarksendPool::IsDenomCompatibleWithSession -- incompatible mode, return false: nState status %d, nSessionUsers status %d \n " , nState ! = POOL_STATE_ACCEPTING_ENTRIES , nSessionUsers > = GetMaxPoolTransactions ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( nDenom ! = nSessionDenom ) {
nErrorID = ERR_DENOM ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::IsDenomCompatibleWithSession -- compatible \n " ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
nSessionUsers + + ;
nLastTimeChanged = GetTimeMillis ( ) ;
2014-12-09 02:17:57 +01:00
vecSessionCollateral . push_back ( txCollateral ) ;
return true ;
}
2016-08-05 21:49:45 +02:00
/* Create a nice string to show the denominations
Function returns as follows :
( bit on if present )
bit 0 - 100
bit 1 - 10
bit 2 - 1
bit 3 - .1
none of above - non - denom
bit 4 and so on - non - denom
*/
std : : string CDarksendPool : : GetDenominationsToString ( int nDenom )
{
std : : string strDenom ;
2014-12-28 00:45:07 +01:00
2016-08-05 21:49:45 +02:00
if ( nDenom & ( 1 < < 0 ) ) strDenom + = " 100 " ;
2014-12-28 00:45:07 +01:00
if ( nDenom & ( 1 < < 1 ) ) {
if ( strDenom . size ( ) > 0 ) strDenom + = " + " ;
strDenom + = " 10 " ;
}
if ( nDenom & ( 1 < < 2 ) ) {
if ( strDenom . size ( ) > 0 ) strDenom + = " + " ;
strDenom + = " 1 " ;
}
if ( nDenom & ( 1 < < 3 ) ) {
if ( strDenom . size ( ) > 0 ) strDenom + = " + " ;
strDenom + = " 0.1 " ;
}
2016-08-05 21:49:45 +02:00
if ( strDenom . size ( ) = = 0 & & nDenom > = ( 1 < < 4 ) ) strDenom + = " non-denom " ;
return strDenom ;
2014-12-28 00:45:07 +01:00
}
2016-08-05 21:49:45 +02:00
int CDarksendPool : : GetDenominations ( const std : : vector < CTxDSOut > & vecTxDSOut )
{
std : : vector < CTxOut > vecTxOut ;
2015-03-06 23:17:51 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CTxDSOut out , vecTxDSOut )
vecTxOut . push_back ( out ) ;
2015-03-06 23:17:51 +01:00
2016-08-05 21:49:45 +02:00
return GetDenominations ( vecTxOut ) ;
2015-03-06 23:17:51 +01:00
}
2016-08-05 21:49:45 +02:00
/* Return a bitshifted integer representing the denominations in this list
Function returns as follows :
( bit on if present )
100 - bit 0
10 - bit 1
1 - bit 2
.1 - bit 3
non - denom - 0 , all bits off
*/
int CDarksendPool : : GetDenominations ( const std : : vector < CTxOut > & vecTxOut , bool fSingleRandomDenom )
{
std : : vector < std : : pair < CAmount , int > > vecDenomUsed ;
2014-12-09 02:17:57 +01:00
// make a list of denominations, with zero uses
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CAmount nDenomValue , vecPrivateSendDenominations )
vecDenomUsed . push_back ( std : : make_pair ( nDenomValue , 0 ) ) ;
2014-12-09 02:17:57 +01:00
// look for denominations and update uses to 1
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CTxOut txout , vecTxOut ) {
2014-12-09 02:17:57 +01:00
bool found = false ;
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( PAIRTYPE ( CAmount , int ) & s , vecDenomUsed ) {
if ( txout . nValue = = s . first ) {
2014-12-09 02:17:57 +01:00
s . second = 1 ;
found = true ;
}
}
2015-01-15 15:41:56 +01:00
if ( ! found ) return 0 ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
int nDenom = 0 ;
2014-12-09 02:17:57 +01:00
int c = 0 ;
2016-08-05 21:49:45 +02:00
// if the denomination is used, shift the bit on
BOOST_FOREACH ( PAIRTYPE ( CAmount , int ) & s , vecDenomUsed ) {
int bit = ( fSingleRandomDenom ? insecure_rand ( ) % 2 : 1 ) & s . second ;
nDenom | = bit < < c + + ;
2015-08-10 01:28:43 +02:00
if ( fSingleRandomDenom & & bit ) break ; // use just one random denomination
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
return nDenom ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
int CDarksendPool : : GetDenominationsByAmounts ( const std : : vector < CAmount > & vecAmount )
{
CScript scriptTmp = CScript ( ) ;
std : : vector < CTxOut > vecTxOut ;
2015-01-19 22:25:03 +01:00
2016-08-05 21:49:45 +02:00
BOOST_REVERSE_FOREACH ( CAmount nAmount , vecAmount ) {
CTxOut txout ( nAmount , scriptTmp ) ;
vecTxOut . push_back ( txout ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
return GetDenominations ( vecTxOut , true ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
std : : string CDarksendPool : : GetMessageByID ( int 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 _ ( " Value more than PrivateSend pool maximum allows. " ) ;
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. " ) ;
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_SUCCESS : return _ ( " Transaction created successfully. " ) ;
case MSG_ENTRIES_ADDED : return _ ( " Your entries added successfully. " ) ;
case MSG_NOERR : return _ ( " No errors detected. " ) ;
default : return _ ( " Unknown response. " ) ;
2015-04-15 00:40:28 +02:00
}
}
2016-08-05 21:49:45 +02:00
bool CDarkSendSigner : : IsVinAssociatedWithPubkey ( const CTxIn & txin , const CPubKey & pubkey )
{
CScript payee ;
payee = GetScriptForDestination ( pubkey . GetID ( ) ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CTransaction tx ;
2014-12-09 02:17:57 +01:00
uint256 hash ;
2016-08-05 21:49:45 +02:00
if ( GetTransaction ( txin . prevout . hash , tx , Params ( ) . GetConsensus ( ) , hash , true ) ) {
BOOST_FOREACH ( CTxOut out , tx . vout )
if ( out . nValue = = 1000 * COIN & & out . scriptPubKey = = payee ) return true ;
2014-12-09 02:17:57 +01:00
}
return false ;
}
2016-08-19 13:50:04 +02:00
bool CDarkSendSigner : : GetKeysFromSecret ( std : : string strSecret , CKey & keyRet , CPubKey & pubkeyRet )
2016-08-05 21:49:45 +02:00
{
2014-12-09 02:17:57 +01:00
CBitcoinSecret vchSecret ;
2016-08-19 13:50:04 +02:00
if ( ! vchSecret . SetString ( strSecret ) ) return false ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
keyRet = vchSecret . GetKey ( ) ;
pubkeyRet = keyRet . GetPubKey ( ) ;
2014-12-09 02:17:57 +01:00
return true ;
}
2016-08-19 13:50:04 +02:00
bool CDarkSendSigner : : SignMessage ( std : : string strMessage , std : : vector < unsigned char > & vchSigRet , CKey key )
2014-12-09 02:17:57 +01:00
{
CHashWriter ss ( SER_GETHASH , 0 ) ;
ss < < strMessageMagic ;
ss < < strMessage ;
2016-08-19 13:50:04 +02:00
return key . SignCompact ( ss . GetHash ( ) , vchSigRet ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-12 07:43:18 +02:00
bool CDarkSendSigner : : VerifyMessage ( CPubKey pubkey , const std : : vector < unsigned char > & vchSig , std : : string strMessage , std : : string & strErrorRet )
2014-12-09 02:17:57 +01:00
{
CHashWriter ss ( SER_GETHASH , 0 ) ;
ss < < strMessageMagic ;
ss < < strMessage ;
2016-08-05 21:49:45 +02:00
CPubKey pubkeyFromSig ;
if ( ! pubkeyFromSig . RecoverCompact ( ss . GetHash ( ) , vchSig ) ) {
2016-08-19 13:50:04 +02:00
strErrorRet = " Error recovering public key. " ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( pubkeyFromSig . GetID ( ) ! = pubkey . GetID ( ) ) {
2016-08-19 13:50:04 +02:00
strErrorRet = strprintf ( " Keys don't match: pubkey=%s, pubkeyFromSig=%s, strMessage=%s, vchSig=%s " ,
2016-08-05 21:49:45 +02:00
pubkey . GetID ( ) . ToString ( ) , pubkeyFromSig . GetID ( ) . ToString ( ) , strMessage ,
2016-06-02 08:11:56 +02:00
EncodeBase64 ( & vchSig [ 0 ] , vchSig . size ( ) ) ) ;
return false ;
}
2015-01-25 22:18:26 +01:00
2016-06-02 08:11:56 +02:00
return true ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
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 ;
}
2014-12-09 02:17:57 +01:00
bool CDarksendQueue : : Sign ( )
{
if ( ! fMasterNode ) return false ;
2016-08-05 21:49:45 +02:00
std : : string strMessage = vin . ToString ( ) + boost : : lexical_cast < std : : string > ( nDenom ) + boost : : lexical_cast < std : : string > ( nTime ) + boost : : lexical_cast < std : : string > ( fReady ) ;
2014-12-09 02:17:57 +01:00
2016-08-19 13:50:04 +02:00
if ( ! darkSendSigner . SignMessage ( strMessage , vchSig , activeMasternode . keyMasternode ) ) {
2016-08-29 21:17:00 +02:00
LogPrintf ( " CDarksendQueue::Sign -- SignMessage() failed, %s \n " , ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
return CheckSignature ( ) ;
}
bool CDarksendQueue : : CheckSignature ( )
{
CMasternode * pmn = mnodeman . Find ( vin ) ;
if ( pmn = = NULL ) 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 ) ;
2016-08-12 07:43:18 +02:00
std : : string strError = " " ;
2016-08-05 21:49:45 +02:00
2016-08-12 07:43:18 +02:00
if ( ! darkSendSigner . VerifyMessage ( pmn - > pubkey2 , vchSig , strMessage , strError ) ) {
2016-08-29 21:17:00 +02:00
LogPrintf ( " CDarksendQueue::CheckSignature -- Got bad Masternode queue signature: %s; error: %s \n " , ToString ( ) , strError ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
return true ;
}
2016-08-05 21:49:45 +02:00
bool CDarksendQueue : : GetAddress ( CService & addrRet )
{
CMasternode * pmn = mnodeman . Find ( vin ) ;
if ( pmn = = NULL ) return false ;
addrRet = pmn - > addr ;
return true ;
}
bool CDarksendQueue : : GetProtocolVersion ( int & nProtocolVersionRet )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
CMasternode * pmn = mnodeman . Find ( vin ) ;
if ( pmn = = NULL ) return false ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
nProtocolVersionRet = pmn - > protocolVersion ;
return true ;
}
bool CDarksendQueue : : Relay ( )
{
2014-12-09 02:17:57 +01:00
LOCK ( cs_vNodes ) ;
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( CNode * pnode , vNodes )
2016-02-17 23:18:57 +01:00
pnode - > PushMessage ( NetMsgType : : DSQUEUE , ( * this ) ) ;
2014-12-09 02:17:57 +01:00
return true ;
}
2016-08-05 21:49:45 +02:00
bool CDarksendBroadcastTx : : Sign ( )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
if ( ! fMasterNode ) return false ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
std : : string strMessage = tx . GetHash ( ) . ToString ( ) + boost : : lexical_cast < std : : string > ( sigTime ) ;
2015-02-23 21:01:21 +01:00
2016-08-19 13:50:04 +02:00
if ( ! darkSendSigner . SignMessage ( strMessage , vchSig , activeMasternode . keyMasternode ) ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendBroadcastTx::Sign -- SignMessage() failed \n " ) ;
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
return CheckSignature ( ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
bool CDarksendBroadcastTx : : CheckSignature ( )
{
CMasternode * pmn = mnodeman . Find ( vin ) ;
if ( pmn = = NULL ) return false ;
std : : string strMessage = tx . GetHash ( ) . ToString ( ) + boost : : lexical_cast < std : : string > ( sigTime ) ;
std : : string strError = " " ;
if ( ! darkSendSigner . VerifyMessage ( pmn - > pubkey2 , vchSig , strMessage , strError ) ) {
2016-08-19 13:50:04 +02:00
LogPrintf ( " CDarksendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s \n " , strError ) ;
2016-08-05 21:49:45 +02:00
return false ;
}
return true ;
}
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
void CDarksendPool : : RelayFinalTransaction ( const CTransaction & txFinal )
2015-03-02 00:09:33 +01:00
{
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodes )
2016-08-05 21:49:45 +02:00
pnode - > PushMessage ( NetMsgType : : DSFINALTX , nSessionID , txFinal ) ;
2015-03-02 00:09:33 +01:00
}
2016-08-29 21:14:34 +02:00
void CDarksendPool : : RelayIn ( const CDarkSendEntry & entry )
2015-03-02 00:09:33 +01:00
{
2015-08-04 22:51:16 +02:00
if ( ! pSubmittedToMasternode ) return ;
2015-03-02 00:09:33 +01:00
2015-08-04 22:51:16 +02:00
CNode * pnode = FindNode ( pSubmittedToMasternode - > addr ) ;
if ( pnode ! = NULL ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::RelayIn -- found master, relaying message to %s \n " , pnode - > addr . ToString ( ) ) ;
2016-08-29 21:14:34 +02:00
pnode - > PushMessage ( NetMsgType : : DSVIN , entry ) ;
2015-03-02 00:09:33 +01:00
}
}
2016-08-05 21:49:45 +02:00
void CDarksendPool : : RelayStatus ( int nErrorID )
2015-03-02 00:09:33 +01:00
{
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodes )
2016-08-05 21:49:45 +02:00
pnode - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , nState , nEntriesCount , nAcceptedEntriesCount , nErrorID ) ;
2015-03-02 00:09:33 +01:00
}
2016-08-05 21:49:45 +02:00
void CDarksendPool : : RelayCompletedTransaction ( bool fError , int nErrorID )
2015-03-02 00:09:33 +01:00
{
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodes )
2016-08-05 21:49:45 +02:00
pnode - > PushMessage ( NetMsgType : : DSCOMPLETE , nSessionID , fError , nErrorID ) ;
}
void CDarksendPool : : SetState ( unsigned int nStateNew )
{
if ( fMasterNode & & ( nStateNew = = POOL_STATE_ERROR | | nStateNew = = POOL_STATE_SUCCESS ) ) {
LogPrint ( " privatesend " , " CDarksendPool::SetState -- Can't set state to ERROR or SUCCESS as a Masternode. \n " ) ;
return ;
}
LogPrintf ( " CDarksendPool::SetState -- nState: %d, nStateNew: %d \n " , nState , nStateNew ) ;
if ( nState ! = nStateNew ) {
nLastTimeChanged = GetTimeMillis ( ) ;
if ( fMasterNode ) {
RelayStatus ( MASTERNODE_RESET ) ;
}
}
nState = nStateNew ;
2015-03-02 00:09:33 +01:00
}
2016-03-02 22:20:04 +01:00
void CDarksendPool : : UpdatedBlockTip ( const CBlockIndex * pindex )
{
pCurrentBlockIndex = pindex ;
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::UpdatedBlockTip -- pCurrentBlockIndex->nHeight: %d \n " , pCurrentBlockIndex - > nHeight ) ;
2016-03-02 22:20:04 +01:00
2016-08-29 21:11:34 +02:00
if ( ! fLiteMode & & masternodeSync . IsMasternodeListSynced ( ) ) {
2016-03-02 22:20:04 +01:00
NewBlock ( ) ;
2016-08-28 12:12:14 +02:00
}
2016-03-02 22:20:04 +01:00
}
2015-02-09 20:28:29 +01:00
//TODO: Rename/move to core
2014-12-09 02:17:57 +01:00
void ThreadCheckDarkSendPool ( )
{
2016-06-27 17:25:22 +02:00
if ( fLiteMode ) return ; // disable all Dash specific functionality
2015-01-18 16:28:16 +01:00
2016-03-02 12:17:17 +01:00
static bool fOneThread ;
2016-08-05 21:49:45 +02:00
if ( fOneThread ) return ;
2016-03-02 12:17:17 +01:00
fOneThread = true ;
2016-06-27 17:25:22 +02:00
// Make this thread recognisable as the PrivateSend thread
RenameThread ( " dash-privatesend " ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
unsigned int nTick = 0 ;
2016-09-01 09:03:47 +02:00
unsigned int nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN ;
2014-12-06 20:41:53 +01:00
2014-12-09 02:17:57 +01:00
while ( true )
{
MilliSleep ( 1000 ) ;
2015-03-04 19:17:30 +01:00
2015-07-26 00:43:40 +02:00
// try to sync from all available nodes, one step at a time
2016-08-28 12:12:14 +02:00
masternodeSync . ProcessTick ( ) ;
2015-07-15 04:44:58 +02:00
2016-08-05 21:49:45 +02:00
if ( masternodeSync . IsBlockchainSynced ( ) & & ! ShutdownRequested ( ) ) {
2015-07-23 04:19:37 +02:00
2016-08-05 21:49:45 +02:00
nTick + + ;
2015-07-23 04:19:37 +02:00
// check if we should activate or ping every few minutes,
// start right after sync is considered to be done
2016-08-05 21:49:45 +02:00
if ( nTick % MASTERNODE_MIN_MNP_SECONDS = = 1 )
activeMasternode . ManageState ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( nTick % 60 = = 0 ) {
2015-08-05 00:49:34 +02:00
mnodeman . CheckAndRemove ( ) ;
2015-07-14 08:10:18 +02:00
mnodeman . ProcessMasternodeConnections ( ) ;
2016-04-13 19:49:47 +02:00
mnpayments . CheckAndRemove ( ) ;
2015-07-14 08:10:18 +02:00
CleanTransactionLocksList ( ) ;
}
darkSendPool . CheckTimeout ( ) ;
darkSendPool . CheckForCompleteQueue ( ) ;
2016-08-05 21:49:45 +02:00
if ( nDoAutoNextRun = = nTick ) {
if ( darkSendPool . GetState ( ) = = POOL_STATE_IDLE )
darkSendPool . DoAutomaticDenominating ( ) ;
2016-09-01 09:03:47 +02:00
nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + insecure_rand ( ) % ( PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN ) ;
2015-07-14 08:10:18 +02:00
}
2014-12-09 02:17:57 +01:00
}
}
}