2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 The Dash Core developers
2024-12-16 19:40:22 +01:00
// Copyright (c) 2021-2024 The Neobytes 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"
2016-10-16 21:23:17 +02:00
# include "utilmoneystr.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 ;
2016-11-20 07:52:23 +01:00
int nLiquidityProvider = DEFAULT_PRIVATESEND_LIQUIDITY ;
2016-08-05 21:49:45 +02:00
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
{
2024-12-16 19:40:22 +01:00
if ( fLiteMode ) return ; // ignore all Neobytes 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
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSACCEPT -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_VERSION ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( ! fMasterNode ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSACCEPT -- not a Masternode! \n " ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_NOT_A_MN ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-11-07 00:27:10 +01:00
if ( IsSessionReady ( ) ) {
// too many users in this session already, reject new ones
LogPrintf ( " DSACCEPT -- queue is already full! \n " ) ;
PushStatus ( pfrom , STATUS_ACCEPTED , ERR_QUEUE_FULL ) ;
return ;
}
2014-12-09 02:17:57 +01:00
int nDenom ;
CTransaction txCollateral ;
vRecv > > nDenom > > txCollateral ;
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSACCEPT -- nDenom %d (%s) txCollateral %s " , nDenom , GetDenominationsToString ( nDenom ) , txCollateral . ToString ( ) ) ;
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 ) {
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_MN_LIST ) ;
2014-12-25 20:21:35 +01:00
return ;
2014-12-09 02:17:57 +01:00
}
2016-11-02 16:54:36 +01:00
if ( vecSessionCollaterals . size ( ) = = 0 & & pmn - > nLastDsq ! = 0 & &
2016-08-05 21:49:45 +02:00
pmn - > nLastDsq + mnodeman . CountEnabled ( MIN_PRIVATESEND_PEER_PROTO_VERSION ) / 5 > mnodeman . nDsqCount )
{
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSACCEPT -- last dsq too recent, must wait: addr=%s \n " , pfrom - > addr . ToString ( ) ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_RECENT ) ;
2016-08-05 21:49:45 +02:00
return ;
2014-12-09 02:17:57 +01:00
}
2016-11-02 16:54:36 +01:00
PoolMessage nMessageID = MSG_NOERR ;
2016-10-27 23:06:33 +02:00
bool fResult = nSessionID = = 0 ? CreateNewSession ( nDenom , txCollateral , nMessageID )
: AddUserToExistingSession ( nDenom , txCollateral , nMessageID ) ;
if ( fResult ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSACCEPT -- is compatible, please submit! \n " ) ;
PushStatus ( pfrom , STATUS_ACCEPTED , nMessageID ) ;
2014-12-09 02:17:57 +01:00
return ;
} else {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSACCEPT -- not compatible with existing transactions! \n " ) ;
PushStatus ( pfrom , STATUS_REJECTED , nMessageID ) ;
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 ) {
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " DSQUEUE -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2014-12-09 02:17:57 +01:00
return ;
}
CDarksendQueue dsq ;
vRecv > > dsq ;
2016-11-07 00:27:26 +01:00
// process every dsq only once
BOOST_FOREACH ( CDarksendQueue q , vecDarksendQueue ) {
if ( q = = dsq ) {
// LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString());
return ;
}
}
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSQUEUE -- %s new \n " , dsq . ToString ( ) ) ;
2016-11-07 00:27:26 +01:00
if ( dsq . IsExpired ( ) | | dsq . nTime > GetTime ( ) + PRIVATESEND_QUEUE_TIMEOUT ) 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
2016-11-02 16:54:36 +01:00
if ( ! dsq . CheckSignature ( pmn - > pubKeyMasternode ) ) {
// we probably have outdated info
mnodeman . AskForMN ( pfrom , dsq . vin ) ;
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-11-02 16:54:36 +01:00
if ( ( CNetAddr ) pSubmittedToMasternode - > addr ! = ( CNetAddr ) pmn - > addr ) {
LogPrintf ( " DSQUEUE -- message doesn't match current Masternode: pSubmittedToMasternode=%s, addr=%s \n " , pSubmittedToMasternode - > addr . ToString ( ) , pmn - > 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-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSQUEUE -- PrivateSend queue (%s) is ready on masternode %s \n " , dsq . ToString ( ) , pmn - > 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-11-07 00:27:26 +01:00
BOOST_FOREACH ( CDarksendQueue q , vecDarksendQueue ) {
if ( q . vin = = dsq . vin ) {
// no way same mn can send another "not yet ready" dsq this soon
LogPrint ( " privatesend " , " DSQUEUE -- Masternode %s is sending WAY too many dsq messages \n " , pmn - > addr . ToString ( ) ) ;
return ;
}
}
2014-12-25 20:21:35 +01:00
2016-11-02 16:54:36 +01:00
int nThreshold = pmn - > nLastDsq + mnodeman . CountEnabled ( MIN_PRIVATESEND_PEER_PROTO_VERSION ) / 5 ;
LogPrint ( " privatesend " , " DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d \n " , pmn - > nLastDsq , nThreshold , mnodeman . nDsqCount ) ;
2014-12-09 02:17:57 +01:00
//don't allow a few nodes to dominate the queuing process
2016-11-02 16:54:36 +01:00
if ( pmn - > nLastDsq ! = 0 & & nThreshold > mnodeman . nDsqCount ) {
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " 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 ;
2016-09-16 00:00:06 +02:00
pmn - > fAllowMixingTx = true ;
2014-12-09 02:17:57 +01:00
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSQUEUE -- new PrivateSend queue (%s) from masternode %s \n " , dsq . ToString ( ) , pmn - > addr . ToString ( ) ) ;
2016-11-07 00:27:26 +01:00
if ( pSubmittedToMasternode & & pSubmittedToMasternode - > vin . prevout = = dsq . vin . prevout ) {
dsq . fTried = true ;
}
2014-12-09 02:17:57 +01:00
vecDarksendQueue . push_back ( dsq ) ;
dsq . Relay ( ) ;
}
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSVIN ) {
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_VERSION ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( ! fMasterNode ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- not a Masternode! \n " ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_NOT_A_MN ) ;
2014-12-09 02:17:57 +01:00
return ;
}
//do we have enough users in the current session?
2016-11-02 16:54:36 +01:00
if ( ! IsSessionReady ( ) ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- session not complete! \n " ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_SESSION ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-11-07 00:27:10 +01:00
CDarkSendEntry entry ;
vRecv > > entry ;
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSVIN -- txCollateral %s " , entry . txCollateral . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
//do we have the same denominations as the current session?
2016-08-29 21:14:34 +02:00
if ( ! IsOutputsCompatibleWithSessionDenom ( entry . vecTxDSOut ) ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- not compatible with existing transactions! \n " ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_EXISTING_TX ) ;
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 ;
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 ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- non-standard pubkey detected! scriptPubKey=%s \n " , ScriptToAsmStr ( txout . scriptPubKey ) ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_NON_STANDARD_PUBKEY ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( ! txout . scriptPubKey . IsNormalPaymentScript ( ) ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- invalid script! scriptPubKey=%s \n " , ScriptToAsmStr ( txout . scriptPubKey ) ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_INVALID_SCRIPT ) ;
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-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " 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 {
2016-11-02 16:54:36 +01:00
LogPrintf ( " DSVIN -- missing input! tx=%s " , tx . ToString ( ) ) ;
PushStatus ( pfrom , STATUS_REJECTED , ERR_MISSING_TX ) ;
return ;
2014-12-09 02:17:57 +01:00
}
}
2016-09-01 09:03:47 +02:00
if ( nValueIn > PRIVATESEND_POOL_MAX ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- more than PrivateSend pool max! nValueIn: %lld, tx=%s " , nValueIn , tx . ToString ( ) ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_MAXIMUM ) ;
2016-08-05 21:49:45 +02:00
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 ( ) ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- fees are too high! fees: %lld, tx=%s " , nValueIn - nValueOut , tx . ToString ( ) ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_FEES ) ;
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-10-20 23:11:57 +02:00
LogPrintf ( " DSVIN -- transaction not valid! tx=%s " , tx . ToString ( ) ) ;
2016-11-02 16:54:36 +01:00
PushStatus ( pfrom , STATUS_REJECTED , ERR_INVALID_TX ) ;
2015-07-30 15:44:18 +02:00
return ;
}
2014-12-09 02:17:57 +01:00
}
}
2016-11-02 16:54:36 +01:00
PoolMessage nMessageID = MSG_NOERR ;
2016-10-20 23:11:57 +02:00
if ( AddEntry ( entry , nMessageID ) ) {
PushStatus ( pfrom , STATUS_ACCEPTED , nMessageID ) ;
2016-08-05 21:49:45 +02:00
CheckPool ( ) ;
2016-10-27 23:06:33 +02:00
RelayStatus ( STATUS_ACCEPTED ) ;
2014-12-09 02:17:57 +01:00
} else {
2016-10-20 23:11:57 +02:00
PushStatus ( pfrom , STATUS_REJECTED , nMessageID ) ;
2016-10-27 23:06:33 +02:00
SetNull ( ) ;
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 ) {
2016-11-02 16:54:36 +01:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSSTATUSUPDATE -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-11-07 00:27:10 +01:00
if ( fMasterNode ) {
// LogPrintf("DSSTATUSUPDATE -- Can't run on a Masternode!\n");
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 ) {
2016-10-20 23:11:57 +02:00
//LogPrintf("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 ;
2016-10-20 23:11:57 +02:00
int nMsgStatusUpdate ;
int nMsgMessageID ;
vRecv > > nMsgSessionID > > nMsgState > > nMsgEntriesCount > > nMsgStatusUpdate > > nMsgMessageID ;
2014-12-09 02:17:57 +01:00
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " DSSTATUSUPDATE -- nMsgSessionID %d nMsgState: %d nEntriesCount: %d nMsgStatusUpdate: %d nMsgMessageID %d \n " ,
nMsgSessionID , nMsgState , nEntriesCount , nMsgStatusUpdate , nMsgMessageID ) ;
2014-12-09 02:17:57 +01:00
2016-10-20 23:11:57 +02:00
if ( nMsgState < POOL_STATE_MIN | | nMsgState > POOL_STATE_MAX ) {
LogPrint ( " privatesend " , " DSSTATUSUPDATE -- nMsgState is out of bounds: %d \n " , nMsgState ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2014-12-25 20:21:35 +01:00
2016-10-27 23:06:33 +02:00
if ( nMsgStatusUpdate < STATUS_REJECTED | | nMsgStatusUpdate > STATUS_ACCEPTED ) {
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " DSSTATUSUPDATE -- nMsgStatusUpdate is out of bounds: %d \n " , nMsgStatusUpdate ) ;
return ;
}
if ( nMsgMessageID < MSG_POOL_MIN | | nMsgMessageID > MSG_POOL_MAX ) {
LogPrint ( " privatesend " , " DSSTATUSUPDATE -- nMsgMessageID is out of bounds: %d \n " , nMsgMessageID ) ;
2024-12-16 19:40:22 +01:00
if ( pfrom - > nVersion < 70203 ) nMsgMessageID = MSG_NOERR ;
2016-10-20 23:11:57 +02:00
return ;
}
LogPrint ( " privatesend " , " DSSTATUSUPDATE -- GetMessageByID: %s \n " , GetMessageByID ( PoolMessage ( nMsgMessageID ) ) ) ;
2016-11-11 09:28:35 +01:00
if ( ! CheckPoolStateUpdate ( PoolState ( nMsgState ) , nMsgEntriesCount , PoolStatusUpdate ( nMsgStatusUpdate ) , PoolMessage ( nMsgMessageID ) , nMsgSessionID ) ) {
LogPrint ( " privatesend " , " DSSTATUSUPDATE -- CheckPoolStateUpdate failed \n " ) ;
2016-10-20 23:11:57 +02: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 ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSSIGNFINALTX -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-11-07 00:27:10 +01:00
if ( ! fMasterNode ) {
LogPrintf ( " DSSIGNFINALTX -- not a Masternode! \n " ) ;
return ;
}
2016-08-05 21:49:45 +02:00
std : : vector < CTxIn > vecTxIn ;
vRecv > > vecTxIn ;
2014-12-09 02:17:57 +01:00
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSSIGNFINALTX -- vecTxIn.size() %s \n " , vecTxIn . size ( ) ) ;
2016-08-05 21:49:45 +02:00
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 ) ) {
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d \n " , nTxInIndex , nTxInsCount , nSessionID ) ;
2016-10-27 23:06:33 +02:00
RelayStatus ( STATUS_REJECTED ) ;
2016-10-20 23:11:57 +02:00
return ;
2016-05-24 01:23:43 +02:00
}
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " DSSIGNFINALTX -- AddScriptSig() %d/%d success \n " , nTxInIndex , nTxInsCount ) ;
2014-12-09 02:17:57 +01:00
}
2016-10-20 23:11:57 +02:00
// all is good
CheckPool ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
} else if ( strCommand = = NetMsgType : : DSFINALTX ) {
2016-10-20 23:11:57 +02:00
2016-08-05 21:49:45 +02:00
if ( pfrom - > nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSFINALTX -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2015-03-02 00:09:33 +01:00
return ;
}
2016-11-07 00:27:10 +01:00
if ( fMasterNode ) {
// LogPrintf("DSFINALTX -- Can't run on a Masternode!\n");
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 ) {
2016-10-20 23:11:57 +02:00
//LogPrintf("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 ) {
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " DSFINALTX -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d \n " , nSessionID , nMsgSessionID ) ;
2015-03-02 00:09:33 +01:00
return ;
}
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSFINALTX -- txNew %s " , txNew . ToString ( ) ) ;
2015-03-02 00:09:33 +01:00
//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 ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " DSCOMPLETE -- incompatible version! nVersion: %d \n " , pfrom - > nVersion ) ;
2015-03-02 00:09:33 +01:00
return ;
}
2016-11-07 00:27:10 +01:00
if ( fMasterNode ) {
// LogPrintf("DSCOMPLETE -- Can't run on a Masternode!\n");
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 ) {
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " 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-11-07 00:27:51 +01:00
int nMsgSessionID ;
int nMsgMessageID ;
vRecv > > nMsgSessionID > > nMsgMessageID ;
2016-10-20 23:11:57 +02:00
if ( nMsgMessageID < MSG_POOL_MIN | | nMsgMessageID > MSG_POOL_MAX ) {
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSCOMPLETE -- nMsgMessageID is out of bounds: %d \n " , nMsgMessageID ) ;
2016-10-20 23:11:57 +02:00
return ;
}
2015-03-02 00:09:33 +01:00
2016-08-05 21:49:45 +02:00
if ( nSessionID ! = nMsgSessionID ) {
2016-10-20 23:11:57 +02:00
LogPrint ( " privatesend " , " 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-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " DSCOMPLETE -- nMsgSessionID %d nMsgMessageID %d (%s) \n " , nMsgSessionID , nMsgMessageID , GetMessageByID ( PoolMessage ( nMsgMessageID ) ) ) ;
2016-11-07 00:27:51 +01:00
CompletedTransaction ( PoolMessage ( nMsgMessageID ) ) ;
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
*/
2017-02-02 09:50:21 +01:00
/* Disabled
2016-08-05 21:49:45 +02:00
vecPrivateSendDenominations . push_back ( ( 100 * COIN ) + 100000 ) ;
2017-02-02 09:50:21 +01:00
*/
2016-08-05 21:49:45 +02:00
vecPrivateSendDenominations . push_back ( ( 10 * COIN ) + 10000 ) ;
vecPrivateSendDenominations . push_back ( ( 1 * COIN ) + 1000 ) ;
vecPrivateSendDenominations . push_back ( ( .1 * COIN ) + 100 ) ;
vecPrivateSendDenominations . push_back ( ( .01 * COIN ) + 10 ) ;
2017-02-02 09:50:21 +01:00
/* Disabled till we need them
2016-08-05 21:49:45 +02:00
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-11-02 16:54:36 +01:00
vecSessionCollaterals . 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 ;
2016-11-11 09:29:13 +01:00
pSubmittedToMasternode = NULL ;
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-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = 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
}
2016-11-21 21:40:56 +01:00
std : : string CDarksendPool : : GetStateString ( ) const
{
switch ( nState ) {
case POOL_STATE_IDLE : return " IDLE " ;
case POOL_STATE_QUEUE : return " QUEUE " ;
case POOL_STATE_ACCEPTING_ENTRIES : return " ACCEPTING_ENTRIES " ;
case POOL_STATE_SIGNING : return " SIGNING " ;
case POOL_STATE_ERROR : return " ERROR " ;
case POOL_STATE_SUCCESS : return " SUCCESS " ;
default : return " UNKNOWN " ;
}
}
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-11-02 16:54:36 +01:00
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 ) ; ;
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_ERROR :
return _ ( " PrivateSend request incomplete: " ) + " " + strLastMessage + " " + _ ( " Will retry... " ) ;
case POOL_STATE_SUCCESS :
return _ ( " PrivateSend request complete: " ) + " " + strLastMessage ;
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
2016-10-27 23:06:33 +02:00
// If entries are full, create finalized transaction
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES & & GetEntriesCount ( ) > = GetMaxPoolTransactions ( ) ) {
2016-10-27 23:06:33 +02:00
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- FINALIZE TRANSACTIONS \n " ) ;
CreateFinalTransaction ( ) ;
return ;
2015-07-26 00:43:40 +02:00
}
2014-12-09 02:17:57 +01:00
2016-10-27 23:06:33 +02:00
// If we have all of the signatures, try to compile the transaction
if ( nState = = POOL_STATE_SIGNING & & IsSignaturesComplete ( ) ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckPool -- SIGNING \n " ) ;
CommitFinalTransaction ( ) ;
return ;
}
2015-03-02 00:09:33 +01:00
}
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-11-09 23:49:57 +01:00
if ( ( nState = = POOL_STATE_ERROR | | nState = = POOL_STATE_SUCCESS ) & & GetTimeMillis ( ) - nTimeLastSuccessfulStep > = 10000 ) {
2016-08-05 21:49:45 +02:00
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 ( ) ;
2015-03-02 00:09:33 +01:00
}
}
2014-12-09 02:17:57 +01:00
2016-11-02 16:54:36 +01:00
void CDarksendPool : : CreateFinalTransaction ( )
{
LogPrint ( " privatesend " , " CDarksendPool::CreateFinalTransaction -- FINALIZE TRANSACTIONS \n " ) ;
CMutableTransaction txNew ;
// make our new transaction
for ( int i = 0 ; i < GetEntriesCount ( ) ; i + + ) {
BOOST_FOREACH ( const CTxDSOut & txdsout , vecEntries [ i ] . vecTxDSOut )
txNew . vout . push_back ( txdsout ) ;
BOOST_FOREACH ( const CTxDSIn & txdsin , vecEntries [ i ] . vecTxDSIn )
txNew . vin . push_back ( txdsin ) ;
}
// BIP69 https://github.com/kristovatlas/bips/blob/master/bip-0069.mediawiki
sort ( txNew . vin . begin ( ) , txNew . vin . end ( ) ) ;
sort ( txNew . vout . begin ( ) , txNew . vout . end ( ) ) ;
finalMutableTransaction = txNew ;
LogPrint ( " privatesend " , " CDarksendPool::CreateFinalTransaction -- finalMutableTransaction=%s " , txNew . ToString ( ) ) ;
// request signatures from clients
RelayFinalTransaction ( finalMutableTransaction ) ;
SetState ( POOL_STATE_SIGNING ) ;
}
void CDarksendPool : : CommitFinalTransaction ( )
2015-03-02 00:09:33 +01:00
{
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 ) ;
2016-11-02 16:54:36 +01:00
uint256 hashTx = finalTransaction . GetHash ( ) ;
2015-03-02 00:09:33 +01:00
2016-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " CDarksendPool::CommitFinalTransaction -- 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 ;
2016-11-02 16:54:36 +01:00
mempool . PrioritiseTransaction ( hashTx , hashTx . ToString ( ) , 1000 , 0.1 * COIN ) ;
2016-03-27 20:13:08 +02:00
if ( ! lockMain | | ! AcceptToMemoryPool ( mempool , validationState , finalTransaction , false , NULL , false , true , true ) )
2015-07-26 00:43:40 +02:00
{
2016-11-02 16:54:36 +01:00
LogPrintf ( " CDarksendPool::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid \n " ) ;
2015-07-26 00:43:40 +02:00
SetNull ( ) ;
2016-10-27 23:06:33 +02:00
// not much we can do in this case, just notify clients
2016-11-07 00:27:51 +01:00
RelayCompletedTransaction ( 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-11-02 16:54:36 +01:00
LogPrintf ( " CDarksendPool::CommitFinalTransaction -- 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
2016-11-02 16:54:36 +01:00
if ( ! mapDarksendBroadcastTxes . count ( hashTx ) ) {
2016-08-05 21:49:45 +02:00
CDarksendBroadcastTx dstx ( finalTransaction , activeMasternode . vin , GetAdjustedTime ( ) ) ;
dstx . Sign ( ) ;
2016-11-02 16:54:36 +01:00
mapDarksendBroadcastTxes . insert ( std : : make_pair ( hashTx , dstx ) ) ;
2016-03-02 21:26:45 +01:00
}
2014-12-09 02:17:57 +01:00
2016-11-02 16:54:36 +01:00
LogPrintf ( " CDarksendPool::CommitFinalTransaction -- TRANSMITTING DSTX \n " ) ;
2014-12-09 02:17:57 +01:00
2016-11-02 16:54:36 +01:00
CInv inv ( MSG_DSTX , hashTx ) ;
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-11-07 00:27:51 +01:00
RelayCompletedTransaction ( 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-11-02 16:54:36 +01:00
LogPrint ( " privatesend " , " CDarksendPool::CommitFinalTransaction -- COMPLETED -- RESETTING \n " ) ;
2016-03-02 21:26:45 +01:00
SetNull ( ) ;
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-11-20 07:52:45 +01:00
if ( GetRandInt ( 100 ) > 33 ) return ;
2016-11-02 19:05:30 +01:00
std : : vector < CTransaction > vecOffendersCollaterals ;
2015-07-26 00:43:40 +02:00
2016-08-05 21:49:45 +02:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES ) {
2016-11-02 16:54:36 +01:00
BOOST_FOREACH ( const CTransaction & txCollateral , vecSessionCollaterals ) {
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 ) {
2016-11-02 19:05:30 +01:00
LogPrintf ( " CDarksendPool::ChargeFees -- found uncooperative node (didn't send transaction), found offence \n " ) ;
vecOffendersCollaterals . push_back ( txCollateral ) ;
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 ) {
2016-11-02 19:05:30 +01:00
LogPrintf ( " CDarksendPool::ChargeFees -- found uncooperative node (didn't sign), found offence \n " ) ;
vecOffendersCollaterals . push_back ( entry . txCollateral ) ;
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-11-02 19:05:30 +01:00
// no offences found
if ( vecOffendersCollaterals . empty ( ) ) return ;
2014-12-09 02:17:57 +01:00
2016-11-02 19:05:30 +01:00
//mostly offending? Charge sometimes
2016-11-20 07:52:45 +01:00
if ( ( int ) vecOffendersCollaterals . size ( ) > = Params ( ) . PoolMaxTransactions ( ) - 1 & & GetRandInt ( 100 ) > 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-11-02 19:05:30 +01:00
if ( ( int ) vecOffendersCollaterals . size ( ) > = 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-11-02 19:05:30 +01:00
std : : random_shuffle ( vecOffendersCollaterals . begin ( ) , vecOffendersCollaterals . end ( ) ) ;
2015-07-26 00:43:40 +02:00
2016-11-02 19:05:30 +01:00
if ( nState = = POOL_STATE_ACCEPTING_ENTRIES | | nState = = POOL_STATE_SIGNING ) {
LogPrintf ( " CDarksendPool::ChargeFees -- found uncooperative node (didn't %s transaction), charging fees: %s \n " ,
( nState = = POOL_STATE_SIGNING ) ? " sign " : " send " , vecOffendersCollaterals [ 0 ] . ToString ( ) ) ;
2015-07-26 00:43:40 +02:00
2016-11-28 15:21:50 +01:00
LOCK ( cs_main ) ;
2016-11-02 19:05:30 +01:00
CValidationState state ;
bool fMissingInputs ;
if ( ! AcceptToMemoryPool ( mempool , state , vecOffendersCollaterals [ 0 ] , false , & fMissingInputs , false , true ) ) {
// should never really happen
LogPrintf ( " CDarksendPool::ChargeFees -- ERROR: AcceptToMemoryPool failed! \n " ) ;
} else {
RelayTransaction ( vecOffendersCollaterals [ 0 ] ) ;
2014-12-09 02:17:57 +01:00
}
}
}
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
2024-12-16 19:40:22 +01:00
allow endless transaction that would bloat Neobytes and make it unusable . To
2016-08-05 21:49:45 +02:00
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-11-28 15:21:50 +01:00
LOCK ( cs_main ) ;
2016-11-02 16:54:36 +01:00
BOOST_FOREACH ( const CTransaction & txCollateral , vecSessionCollaterals ) {
2016-08-05 21:49:45 +02:00
2016-11-20 07:52:45 +01:00
if ( GetRandInt ( 100 ) > 10 ) return ;
2016-08-05 21:49:45 +02:00
2016-11-02 19:05:30 +01:00
LogPrintf ( " CDarksendPool::ChargeRandomFees -- charging random fees, txCollateral=%s " , txCollateral . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
2016-11-02 19:05:30 +01:00
CValidationState state ;
bool fMissingInputs ;
if ( ! AcceptToMemoryPool ( mempool , state , txCollateral , false , & fMissingInputs , false , true ) ) {
// should never really happen
LogPrintf ( " CDarksendPool::ChargeRandomFees -- ERROR: AcceptToMemoryPool failed! \n " ) ;
} else {
RelayTransaction ( txCollateral ) ;
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 ( )
{
2016-11-28 15:21:50 +01:00
{
TRY_LOCK ( cs_darksend , lockDS ) ;
if ( ! lockDS ) return ; // it's ok to fail here, we run this quite frequently
// check mixing queue objects for timeouts
std : : vector < CDarksendQueue > : : iterator it = vecDarksendQueue . begin ( ) ;
while ( it ! = vecDarksendQueue . end ( ) ) {
if ( ( * it ) . IsExpired ( ) ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- Removing expired queue (%s) \n " , ( * it ) . ToString ( ) ) ;
it = vecDarksendQueue . erase ( it ) ;
} else + + it ;
}
2016-03-08 12:09:22 +01:00
}
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_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 ;
2016-10-20 23:11:57 +02:00
default :
break ;
2014-12-25 20:21:35 +01:00
}
2014-12-09 02:17:57 +01:00
}
2016-10-27 23:06:33 +02:00
int nLagTime = fMasterNode ? 0 : 10000 ; // if we're the client, give the server a few extra seconds before resetting.
int nTimeout = ( nState = = POOL_STATE_SIGNING ) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT ;
2016-11-09 23:49:57 +01:00
bool fTimeout = GetTimeMillis ( ) - nTimeLastSuccessfulStep > = nTimeout * 1000 + nLagTime ;
2014-12-09 02:17:57 +01:00
2016-10-27 23:06:33 +02:00
if ( nState ! = POOL_STATE_IDLE & & fTimeout ) {
LogPrint ( " privatesend " , " CDarksendPool::CheckTimeout -- %s timed out (%ds) -- restting \n " ,
( nState = = POOL_STATE_SIGNING ) ? " Signing " : " Session " , nTimeout ) ;
2016-08-05 21:49:45 +02:00
ChargeFees ( ) ;
UnlockCoins ( ) ;
SetNull ( ) ;
SetState ( POOL_STATE_ERROR ) ;
2016-10-27 23:06:33 +02:00
strLastMessage = _ ( " Session 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-11-02 16:54:36 +01:00
if ( nState = = POOL_STATE_QUEUE & & IsSessionReady ( ) ) {
2016-08-05 21:49:45 +02:00
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
}
}
2016-09-21 16:03:04 +02:00
// Check to make sure a given input matches an input in the pool and its scriptSig is valid
bool CDarksendPool : : IsInputScriptSigValid ( const CTxIn & txin )
2016-08-05 21:49:45 +02:00
{
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-10-23 17:27:23 +02:00
if ( txdsin . prevout = = txin . prevout ) {
2016-08-05 21:49:45 +02:00
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?
2016-09-21 16:03:04 +02:00
txNew . vin [ nTxInIndex ] . scriptSig = txin . scriptSig ;
LogPrint ( " privatesend " , " CDarksendPool::IsInputScriptSigValid -- verifying scriptSig %s \n " , ScriptToAsmStr ( txin . scriptSig ) . substr ( 0 , 24 ) ) ;
2016-08-05 21:49:45 +02:00
if ( ! VerifyScript ( txNew . vin [ nTxInIndex ] . scriptSig , sigPubKey , SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC , MutableTransactionSignatureChecker ( & txNew , nTxInIndex ) ) ) {
2016-09-21 16:03:04 +02:00
LogPrint ( " privatesend " , " CDarksendPool::IsInputScriptSigValid -- VerifyScript() failed on input %d \n " , nTxInIndex ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-09-21 16:03:04 +02:00
} else {
LogPrint ( " privatesend " , " CDarksendPool::IsInputScriptSigValid -- Failed to find matching input in pool, %s \n " , txin . ToString ( ) ) ;
return false ;
2014-12-09 02:17:57 +01:00
}
2016-09-21 16:03:04 +02:00
LogPrint ( " privatesend " , " CDarksendPool::IsInputScriptSigValid -- Successfully validated input and scriptSig \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 )
{
2016-11-02 16:54:36 +01:00
if ( txCollateral . vout . empty ( ) ) return false ;
2014-12-09 02:17:57 +01:00
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-10-20 23:11:57 +02:00
bool CDarksendPool : : AddEntry ( const CDarkSendEntry & entryNew , PoolMessage & nMessageIDRet )
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-10-20 23:11:57 +02:00
nMessageIDRet = ERR_INVALID_INPUT ;
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-10-20 23:11:57 +02:00
nMessageIDRet = ERR_INVALID_COLLATERAL ;
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-10-20 23:11:57 +02:00
nMessageIDRet = ERR_ENTRIES_FULL ;
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 ) {
2016-10-23 17:27:23 +02:00
if ( txdsin . prevout = = txin . prevout ) {
2016-08-05 21:49:45 +02:00
LogPrint ( " privatesend " , " CDarksendPool::AddEntry -- found in txin \n " ) ;
2016-10-20 23:11:57 +02:00
nMessageIDRet = ERR_ALREADY_HAVE ;
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 " ) ;
2016-10-20 23:11:57 +02:00
nMessageIDRet = MSG_ENTRIES_ADDED ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
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 )
{
2016-10-22 18:52:14 +02:00
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- 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-09-21 16:03:04 +02:00
if ( ! IsInputScriptSigValid ( txinNew ) ) {
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- Invalid scriptSig \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-10-22 18:52:14 +02:00
LogPrint ( " privatesend " , " CDarksendPool::AddScriptSig -- scriptSig=%s new \n " , ScriptToAsmStr ( txinNew . scriptSig ) . substr ( 0 , 24 ) ) ;
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
2015-03-05 09:10:15 +01:00
// we should already be connected to a Masternode
2016-10-27 23:06:33 +02:00
if ( ! nSessionID ) {
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 ) ;
2016-08-29 21:14:34 +02:00
RelayIn ( entry ) ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
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
2016-11-11 09:28:35 +01:00
bool CDarksendPool : : CheckPoolStateUpdate ( PoolState nStateNew , int nEntriesCountNew , PoolStatusUpdate nStatusUpdate , PoolMessage nMessageID , int nSessionIDNew )
2016-08-05 21:49:45 +02:00
{
2014-12-09 02:17:57 +01:00
if ( fMasterNode ) return false ;
2016-10-27 23:06:33 +02:00
// do not update state when mixing client state is one of these
if ( nState = = POOL_STATE_IDLE | | nState = = POOL_STATE_ERROR | | nState = = POOL_STATE_SUCCESS ) return false ;
2014-12-09 02:17:57 +01:00
2016-10-20 23:11:57 +02:00
strAutoDenomResult = _ ( " Masternode: " ) + " " + GetMessageByID ( nMessageID ) ;
2014-12-28 15:46:39 +01:00
2016-10-27 23:06:33 +02:00
// if rejected at any state
if ( nStatusUpdate = = STATUS_REJECTED ) {
2016-11-11 09:28:35 +01:00
LogPrintf ( " CDarksendPool::CheckPoolStateUpdate -- entry is rejected by Masternode \n " ) ;
2016-10-27 23:06:33 +02:00
UnlockCoins ( ) ;
SetNull ( ) ;
SetState ( POOL_STATE_ERROR ) ;
strLastMessage = GetMessageByID ( nMessageID ) ;
return true ;
}
2014-12-09 02:17:57 +01:00
2016-10-27 23:06:33 +02:00
if ( nStatusUpdate = = STATUS_ACCEPTED & & nState = = nStateNew ) {
if ( nStateNew = = POOL_STATE_QUEUE & & nSessionID = = 0 & & nSessionIDNew ! = 0 ) {
// new session id should be set only in POOL_STATE_QUEUE state
2016-08-05 21:49:45 +02:00
nSessionID = nSessionIDNew ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
2016-11-11 09:28:35 +01:00
LogPrintf ( " CDarksendPool::CheckPoolStateUpdate -- set nSessionID to %d \n " , nSessionID ) ;
2016-10-27 23:06:33 +02:00
return true ;
2014-12-09 02:17:57 +01:00
}
2016-10-27 23:06:33 +02:00
else if ( nStateNew = = POOL_STATE_ACCEPTING_ENTRIES & & nEntriesCount ! = nEntriesCountNew ) {
nEntriesCount = nEntriesCountNew ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
2016-10-27 23:06:33 +02:00
fLastEntryAccepted = true ;
2016-11-11 09:28:35 +01:00
LogPrintf ( " CDarksendPool::CheckPoolStateUpdate -- new entry accepted! \n " ) ;
2016-10-27 23:06:33 +02:00
return true ;
2014-12-09 02:17:57 +01:00
}
}
2016-10-27 23:06:33 +02:00
// only situations above are allowed, fail in any other case
return false ;
2014-12-09 02:17:57 +01:00
}
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-10-22 18:52:14 +02:00
bool CDarksendPool : : SignFinalTransaction ( const CTransaction & finalTransactionNew , CNode * pnode )
2016-08-05 21:49:45 +02:00
{
2016-11-02 16:54:36 +01:00
if ( fMasterNode | | pnode = = NULL ) return false ;
2014-12-09 02:17:57 +01:00
2016-03-27 20:13:08 +02:00
finalMutableTransaction = finalTransactionNew ;
2016-11-02 16:54:36 +01:00
LogPrintf ( " CDarksendPool::SignFinalTransaction -- finalMutableTransaction=%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
}
}
2016-11-07 00:27:10 +01:00
}
2014-12-25 20:21:35 +01:00
2016-11-07 00:27:10 +01:00
if ( sigs . empty ( ) ) {
LogPrintf ( " CDarksendPool::SignFinalTransaction -- can't sign anything! \n " ) ;
UnlockCoins ( ) ;
SetNull ( ) ;
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
// push all of our signatures to the Masternode
2016-10-27 23:06:33 +02:00
LogPrintf ( " CDarksendPool::SignFinalTransaction -- pushing sigs to the masternode, finalMutableTransaction=%s " , finalMutableTransaction . ToString ( ) ) ;
pnode - > PushMessage ( NetMsgType : : DSSIGNFINALTX , sigs ) ;
SetState ( POOL_STATE_SIGNING ) ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
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)
2016-11-07 00:27:51 +01:00
void CDarksendPool : : CompletedTransaction ( PoolMessage nMessageID )
2014-12-09 02:17:57 +01:00
{
if ( fMasterNode ) return ;
2016-11-07 00:27:51 +01:00
if ( nMessageID = = MSG_SUCCESS ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " CompletedTransaction -- success \n " ) ;
nCachedLastSuccessBlock = pCurrentBlockIndex - > nHeight ;
2016-11-07 00:27:51 +01:00
} else {
LogPrintf ( " CompletedTransaction -- error \n " ) ;
2014-12-09 02:17:57 +01:00
}
2016-11-07 00:27:51 +01:00
UnlockCoins ( ) ;
SetNull ( ) ;
2016-10-20 23:11:57 +02:00
strLastMessage = GetMessageByID ( nMessageID ) ;
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-09-11 11:02:54 +02:00
if ( ! pwalletMain | | pwalletMain - > IsLocked ( true ) ) return false ;
2016-10-20 23:11:57 +02:00
if ( nState ! = POOL_STATE_IDLE ) return false ;
2016-08-05 21:49:45 +02:00
2016-10-10 23:07:49 +02:00
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) {
strAutoDenomResult = _ ( " Can't mix while sync in progress. " ) ;
return false ;
}
2016-08-05 21:49:45 +02:00
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 ;
}
2016-09-11 11:02:54 +02:00
if ( ! fDryRun & & pwalletMain - > IsLocked ( true ) ) {
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 ;
}
2024-12-16 19:40:22 +01:00
// ** find the coins we'll use
std : : vector < CTxIn > vecTxIn ;
CAmount nValueMin = CENT ;
CAmount nValueIn = 0 ;
2015-04-30 00:06:24 +02:00
2024-12-16 19:40:22 +01:00
CAmount nOnlyDenominatedBalance ;
CAmount nBalanceNeedsDenominated ;
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
2024-12-16 19:40:22 +01:00
nLowestDenom + = PRIVATESEND_COLLATERAL * 4 ;
2016-09-01 09:03:47 +02:00
}
2015-01-21 07:09:04 +01:00
2024-12-16 19:40:22 +01: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
2024-12-16 19:40:22 +01:00
if ( nBalanceNeedsAnonymized < nLowestDenom ) {
2016-08-05 21:49:45 +02:00
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
}
2024-12-16 19:40:22 +01:00
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- nLowestDenom: %f, nBalanceNeedsAnonymized: %f \n " , ( float ) nLowestDenom / COIN , ( float ) nBalanceNeedsAnonymized / COIN ) ;
// select coins that should be given to the pool
if ( ! pwalletMain - > SelectCoinsDark ( nValueMin , nBalanceNeedsAnonymized , vecTxIn , nValueIn , 0 , nPrivateSendRounds ) )
{
if ( pwalletMain - > SelectCoinsDark ( nValueMin , 9999999 * COIN , vecTxIn , nValueIn , - 2 , 0 ) )
{
nOnlyDenominatedBalance = pwalletMain - > GetDenominatedBalance ( true ) + pwalletMain - > GetDenominatedBalance ( ) - pwalletMain - > GetAnonymizedBalance ( ) ;
nBalanceNeedsDenominated = nBalanceNeedsAnonymized - nOnlyDenominatedBalance ;
if ( nBalanceNeedsDenominated > nValueIn ) nBalanceNeedsDenominated = nValueIn ;
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- `SelectCoinsDark` (%f - (%f + %f - %f = %f) ) = %f \n " ,
( float ) nBalanceNeedsAnonymized / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( true ) / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( ) / COIN ,
( float ) pwalletMain - > GetAnonymizedBalance ( ) / COIN ,
( float ) nOnlyDenominatedBalance / COIN ,
( float ) nBalanceNeedsDenominated / COIN ) ;
if ( nBalanceNeedsDenominated < nLowestDenom ) { // most likely we are just waiting for denoms to confirm
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- No funds detected in need of denominating \n " ) ;
strAutoDenomResult = _ ( " No funds detected in need of denominating. " ) ;
return false ;
}
if ( ! fDryRun ) return CreateDenominated ( ) ;
2015-04-30 00:06:24 +02:00
2024-12-16 19:40:22 +01:00
return true ;
} else {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Can't denominate (no compatible inputs left) \n " ) ;
strAutoDenomResult = _ ( " Can't denominate: no compatible inputs left. " ) ;
return false ;
}
}
2014-12-09 02:17:57 +01:00
2015-06-24 18:30:05 +02:00
if ( fDryRun ) return true ;
2024-12-16 19:40:22 +01:00
nOnlyDenominatedBalance = pwalletMain - > GetDenominatedBalance ( true ) + pwalletMain - > GetDenominatedBalance ( ) - pwalletMain - > GetAnonymizedBalance ( ) ;
nBalanceNeedsDenominated = nBalanceNeedsAnonymized - nOnlyDenominatedBalance ;
LogPrint ( " privatesend " , " CDarksendPool::DoAutomaticDenominating -- 'nBalanceNeedsDenominated > 0' (%f - (%f + %f - %f = %f) ) = %f \n " ,
( float ) nBalanceNeedsAnonymized / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( true ) / COIN ,
( float ) pwalletMain - > GetDenominatedBalance ( ) / COIN ,
( float ) pwalletMain - > GetAnonymizedBalance ( ) / COIN ,
( float ) nOnlyDenominatedBalance / COIN ,
( float ) nBalanceNeedsDenominated / COIN ) ;
//check if we have should create more denominated inputs
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-10-27 23:06:33 +02:00
if ( nSessionID ) {
2016-08-05 21:49:45 +02:00
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
2024-12-16 19:40:22 +01:00
if ( ! fPrivateSendMultiSession & & pwalletMain - > GetDenominatedBalance ( true ) > 0 ) { //get denominated unconfirmed inputs
2016-08-05 21:49:45 +02:00
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-11-02 16:54:36 +01:00
int nMnCountEnabled = mnodeman . CountEnabled ( MIN_PRIVATESEND_PEER_PROTO_VERSION ) ;
2016-10-07 18:15:03 +02:00
// If we've used 90% of the Masternode list then drop the oldest first ~30%
2016-11-02 16:54:36 +01:00
int nThreshold_high = nMnCountEnabled * 0.9 ;
2016-10-07 18:15:03 +02:00
int nThreshold_low = nThreshold_high * 0.7 ;
LogPrint ( " privatesend " , " Checking vecMasternodesUsed: size: %d, threshold: %d \n " , ( int ) vecMasternodesUsed . size ( ) , nThreshold_high ) ;
if ( ( int ) vecMasternodesUsed . size ( ) > nThreshold_high ) {
vecMasternodesUsed . erase ( vecMasternodesUsed . begin ( ) , vecMasternodesUsed . begin ( ) + vecMasternodesUsed . size ( ) - nThreshold_low ) ;
LogPrint ( " privatesend " , " vecMasternodesUsed: new size: %d, threshold: %d \n " , ( int ) vecMasternodesUsed . size ( ) , nThreshold_high ) ;
2016-08-05 21:49:45 +02:00
}
2014-12-30 01:09:34 +01:00
2016-11-20 07:52:45 +01:00
bool fUseQueue = GetRandInt ( 100 ) > 33 ;
2016-08-05 21:49:45 +02:00
// 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 ) {
2016-11-07 00:27:26 +01:00
// only try each queue once
if ( dsq . fTried ) continue ;
dsq . fTried = true ;
2016-10-27 23:06:33 +02:00
2016-08-05 21:49:45 +02:00
if ( dsq . IsExpired ( ) ) continue ;
2014-12-09 02:17:57 +01:00
2016-11-02 16:54:36 +01:00
CMasternode * pmn = mnodeman . Find ( dsq . vin ) ;
if ( pmn = = NULL ) {
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- dsq masternode is not in masternode list, masternode=%s \n " , dsq . vin . prevout . ToStringShort ( ) ) ;
continue ;
}
2015-01-19 22:25:03 +01:00
2016-11-02 16:54:36 +01:00
if ( pmn - > nProtocolVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION ) continue ;
2014-12-09 02:17:57 +01:00
2024-12-16 19:40:22 +01:00
// incompatible denom
if ( dsq . nDenom > = ( 1 < < vecPrivateSendDenominations . size ( ) ) ) continue ;
2014-12-09 02:17:57 +01:00
2016-11-07 00:26:51 +01:00
// mixing rate limit i.e. nLastDsq check should already pass in DSQUEUE ProcessMessage
// in order for dsq to get into vecDarksendQueue, so we should be safe to mix already,
// no need for additional verification here
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 ;
2024-12-16 19:40:22 +01:00
// 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 (%s) \n " , dsq . nDenom , GetDenominationsToString ( dsq . nDenom ) ) ;
2016-08-05 21:49:45 +02:00
continue ;
2015-02-26 02:55:27 +01:00
}
2014-12-09 02:17:57 +01:00
2016-09-29 01:47:31 +02:00
vecMasternodesUsed . push_back ( dsq . vin ) ;
2014-12-09 02:17:57 +01:00
2017-02-11 03:57:09 +01:00
CNode * pnodeFound = NULL ;
{
LOCK ( cs_vNodes ) ;
pnodeFound = FindNode ( pmn - > addr ) ;
if ( pnodeFound ) {
if ( pnodeFound - > fDisconnect ) {
continue ;
} else {
pnodeFound - > AddRef ( ) ;
}
}
}
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- attempt to connect to masternode from queue, addr=%s \n " , pmn - > addr . ToString ( ) ) ;
// connect to Masternode and submit the queue request
2017-02-11 03:57:09 +01:00
CNode * pnode = ( pnodeFound & & pnodeFound - > fMasternode ) ? pnodeFound : ConnectNode ( ( CAddress ) pmn - > addr , NULL , true ) ;
2016-10-16 21:23:42 +02:00
if ( pnode ) {
2015-08-04 22:51:16 +02:00
pSubmittedToMasternode = pmn ;
2016-08-05 21:49:45 +02:00
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 ) ;
2016-10-22 18:52:14 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- connected (from queue), sending DSACCEPT: nSessionDenom: %d (%s), addr=%s \n " ,
nSessionDenom , GetDenominationsToString ( nSessionDenom ) , pnode - > addr . ToString ( ) ) ;
2015-08-04 22:51:16 +02:00
strAutoDenomResult = _ ( " Mixing in progress... " ) ;
2016-10-27 23:06:33 +02:00
SetState ( POOL_STATE_QUEUE ) ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
2017-02-11 03:57:09 +01:00
if ( pnodeFound ) {
pnodeFound - > Release ( ) ;
}
2015-08-04 22:51:16 +02:00
return true ;
2014-12-09 02:17:57 +01:00
} else {
2016-10-22 18:52:14 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- can't connect, addr=%s \n " , pmn - > addr . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
strAutoDenomResult = _ ( " Error connecting to Masternode. " ) ;
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 ;
}
2016-09-29 01:47:31 +02:00
vecMasternodesUsed . push_back ( pmn - > vin ) ;
2016-08-05 21:49:45 +02:00
2016-11-02 16:54:36 +01:00
if ( pmn - > nLastDsq ! = 0 & & pmn - > nLastDsq + nMnCountEnabled / 5 > mnodeman . nDsqCount ) {
2016-10-20 23:11:57 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- Too early to mix on this masternode! "
" masternode=%s addr=%s nLastDsq=%d CountEnabled/5=%d nDsqCount=%d \n " ,
pmn - > vin . prevout . ToStringShort ( ) , pmn - > addr . ToString ( ) , pmn - > nLastDsq ,
2016-11-02 16:54:36 +01:00
nMnCountEnabled / 5 , mnodeman . nDsqCount ) ;
2016-08-05 21:49:45 +02:00
nTries + + ;
continue ;
}
2017-02-11 03:57:09 +01:00
CNode * pnodeFound = NULL ;
{
LOCK ( cs_vNodes ) ;
pnodeFound = FindNode ( pmn - > addr ) ;
if ( pnodeFound ) {
if ( pnodeFound - > fDisconnect ) {
nTries + + ;
continue ;
} else {
pnodeFound - > AddRef ( ) ;
}
}
}
2016-08-05 21:49:45 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- attempt %d connection to Masternode %s \n " , nTries , pmn - > addr . ToString ( ) ) ;
2017-02-11 03:57:09 +01:00
CNode * pnode = ( pnodeFound & & pnodeFound - > fMasternode ) ? pnodeFound : ConnectNode ( ( CAddress ) pmn - > addr , NULL , true ) ;
2016-10-16 21:23:42 +02:00
if ( pnode ) {
2016-10-22 18:52:14 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- connected, addr=%s \n " , pmn - > addr . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
pSubmittedToMasternode = pmn ;
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 ) ;
2016-10-22 18:52:14 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- connected, sending DSACCEPT, nSessionDenom: %d (%s) \n " ,
nSessionDenom , GetDenominationsToString ( nSessionDenom ) ) ;
2016-08-05 21:49:45 +02:00
strAutoDenomResult = _ ( " Mixing in progress... " ) ;
2016-10-27 23:06:33 +02:00
SetState ( POOL_STATE_QUEUE ) ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
2017-02-11 03:57:09 +01:00
if ( pnodeFound ) {
pnodeFound - > Release ( ) ;
}
2016-08-05 21:49:45 +02:00
return true ;
} else {
2016-10-22 18:52:14 +02:00
LogPrintf ( " CDarksendPool::DoAutomaticDenominating -- can't connect, addr=%s \n " , pmn - > addr . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
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 )
{
2016-09-11 11:02:54 +02:00
if ( pwalletMain - > IsLocked ( true ) ) {
2016-09-02 14:19:29 +02:00
strErrorRet = " Wallet locked, unable to create transaction! " ;
return false ;
}
2016-11-07 00:27:10 +01:00
if ( GetEntriesCount ( ) > 0 ) {
strErrorRet = " Already have pending entries in the PrivateSend pool " ;
2016-09-02 14:19:29 +02:00
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
*/
2024-12-16 19:40:22 +01:00
bool fSelected = pwalletMain - > SelectCoinsByDenominations ( nSessionDenom , vecPrivateSendDenominations . back ( ) , PRIVATESEND_POOL_MAX , vecTxIn , vCoins , nValueIn , nMinRounds , nMaxRounds ) ;
2016-09-02 14:19:29 +02:00
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 ;
2016-10-16 21:23:17 +02:00
// Try to add every needed denomination, repeat up to 5-9 times.
2016-09-02 14:19:29 +02:00
// 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 ) ;
2024-12-16 19:40:22 +01:00
std : : vector < int > vecBits ;
if ( ! GetDenominationsBits ( nSessionDenom , vecBits ) ) {
strErrorRet = " Incorrect session denom " ;
return false ;
}
2016-10-16 21:23:17 +02:00
while ( nStep < nStepsMax ) {
BOOST_FOREACH ( int nBit , vecBits ) {
CAmount nValueDenom = vecPrivateSendDenominations [ nBit ] ;
if ( nValueLeft - nValueDenom < 0 ) continue ;
// 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 ;
// step is complete
break ;
2016-09-02 14:19:29 +02:00
}
2016-10-16 21:23:17 +02:00
+ + it ;
+ + it2 ;
2016-09-02 14:19:29 +02:00
}
}
if ( nValueLeft = = 0 ) break ;
2016-10-16 21:23:17 +02:00
nStep + + ;
2016-09-02 14:19:29 +02:00
}
{
// 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 ;
}
2016-12-04 21:33:11 +01:00
bool fCreateMixingCollaterals = ! pwalletMain - > HasCollateralInputs ( ) ;
2016-07-15 12:21:20 +02:00
BOOST_FOREACH ( CompactTallyItem & item , vecTally ) {
2016-12-04 21:33:11 +01:00
if ( ! CreateDenominated ( item , fCreateMixingCollaterals ) ) continue ;
2016-07-15 12:21:20 +02:00
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-12-04 21:33:11 +01:00
bool CDarksendPool : : CreateDenominated ( const CompactTallyItem & tallyItem , bool fCreateMixingCollaterals )
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
2016-12-04 21:33:11 +01:00
if ( fCreateMixingCollaterals ) {
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-10-27 23:06:33 +02:00
bool CDarksendPool : : IsAcceptableDenomAndCollateral ( int nDenom , CTransaction txCollateral , PoolMessage & nMessageIDRet )
2014-12-09 02:17:57 +01:00
{
2016-10-27 23:06:33 +02:00
if ( ! fMasterNode ) return false ;
// is denom even smth legit?
std : : vector < int > vecBits ;
if ( ! GetDenominationsBits ( nDenom , vecBits ) ) {
LogPrint ( " privatesend " , " CDarksendPool::IsAcceptableDenomAndCollateral -- denom not valid! \n " ) ;
2016-10-20 23:11:57 +02:00
nMessageIDRet = ERR_DENOM ;
return false ;
}
2015-04-07 01:07:25 +02:00
2016-10-27 23:06:33 +02:00
// check collateral
2016-08-05 21:49:45 +02:00
if ( ! fUnitTest & & ! IsCollateralValid ( txCollateral ) ) {
2016-10-27 23:06:33 +02:00
LogPrint ( " privatesend " , " CDarksendPool::IsAcceptableDenomAndCollateral -- collateral not valid! \n " ) ;
2016-10-20 23:11:57 +02:00
nMessageIDRet = ERR_INVALID_COLLATERAL ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-10-27 23:06:33 +02:00
return true ;
}
2014-12-25 20:21:35 +01:00
2016-10-27 23:06:33 +02:00
bool CDarksendPool : : CreateNewSession ( int nDenom , CTransaction txCollateral , PoolMessage & nMessageIDRet )
{
if ( ! fMasterNode | | nSessionID ! = 0 ) return false ;
2014-12-09 02:17:57 +01:00
2016-10-27 23:06:33 +02:00
// new session can only be started in idle mode
if ( nState ! = POOL_STATE_IDLE ) {
nMessageIDRet = ERR_MODE ;
LogPrintf ( " CDarksendPool::CreateNewSession -- incompatible mode: nState=%d \n " , nState ) ;
return false ;
}
2014-12-09 02:17:57 +01:00
2016-10-27 23:06:33 +02:00
if ( ! IsAcceptableDenomAndCollateral ( nDenom , txCollateral , nMessageIDRet ) ) {
return false ;
}
// start new session
nMessageIDRet = MSG_NOERR ;
2016-11-20 07:52:45 +01:00
nSessionID = GetRandInt ( 999999 ) + 1 ;
2016-10-27 23:06:33 +02:00
nSessionDenom = nDenom ;
2016-11-09 23:49:57 +01:00
SetState ( POOL_STATE_QUEUE ) ;
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
2016-10-27 23:06:33 +02:00
if ( ! fUnitTest ) {
//broadcast that I'm accepting entries, only if it's the first entry through
CDarksendQueue dsq ( nDenom , activeMasternode . vin , GetTime ( ) , false ) ;
LogPrint ( " privatesend " , " CDarksendPool::CreateNewSession -- signing and relaying new queue: %s \n " , dsq . ToString ( ) ) ;
dsq . Sign ( ) ;
dsq . Relay ( ) ;
vecDarksendQueue . push_back ( dsq ) ;
2014-12-09 02:17:57 +01:00
}
2016-10-27 23:06:33 +02:00
vecSessionCollaterals . push_back ( txCollateral ) ;
LogPrintf ( " CDarksendPool::CreateNewSession -- new session created, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d \n " ,
nSessionID , nSessionDenom , GetDenominationsToString ( nSessionDenom ) , vecSessionCollaterals . size ( ) ) ;
return true ;
}
bool CDarksendPool : : AddUserToExistingSession ( int nDenom , CTransaction txCollateral , PoolMessage & nMessageIDRet )
{
if ( ! fMasterNode | | nSessionID = = 0 | | IsSessionReady ( ) ) return false ;
if ( ! IsAcceptableDenomAndCollateral ( nDenom , txCollateral , nMessageIDRet ) ) {
return false ;
}
// we only add new users to an existing session when we are in queue mode
if ( nState ! = POOL_STATE_QUEUE ) {
nMessageIDRet = ERR_MODE ;
LogPrintf ( " CDarksendPool::AddUserToExistingSession -- incompatible mode: nState=%d \n " , nState ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( nDenom ! = nSessionDenom ) {
2016-10-27 23:06:33 +02:00
LogPrintf ( " CDarksendPool::AddUserToExistingSession -- incompatible denom %d (%s) != nSessionDenom %d (%s) \n " ,
nDenom , GetDenominationsToString ( nDenom ) , nSessionDenom , GetDenominationsToString ( nSessionDenom ) ) ;
2016-10-20 23:11:57 +02:00
nMessageIDRet = ERR_DENOM ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-10-27 23:06:33 +02:00
// count new user as accepted to an existing session
2014-12-09 02:17:57 +01:00
2016-10-20 23:11:57 +02:00
nMessageIDRet = MSG_NOERR ;
2016-11-09 23:49:57 +01:00
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
2016-11-02 16:54:36 +01:00
vecSessionCollaterals . push_back ( txCollateral ) ;
2014-12-09 02:17:57 +01:00
2016-10-27 23:06:33 +02:00
LogPrintf ( " CDarksendPool::AddUserToExistingSession -- new user accepted, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d \n " ,
nSessionID , nSessionDenom , GetDenominationsToString ( nSessionDenom ) , vecSessionCollaterals . size ( ) ) ;
2014-12-09 02:17:57 +01:00
return true ;
}
2016-08-05 21:49:45 +02:00
/* Create a nice string to show the denominations
2016-10-16 21:23:17 +02:00
Function returns as follows ( for 4 denominations ) :
2016-08-05 21:49:45 +02:00
( bit on if present )
bit 0 - 100
bit 1 - 10
bit 2 - 1
bit 3 - .1
2016-10-16 21:23:17 +02:00
bit 4 and so on - out - of - bounds
2016-08-05 21:49:45 +02:00
none of above - non - denom
*/
std : : string CDarksendPool : : GetDenominationsToString ( int nDenom )
{
2016-10-16 21:23:17 +02:00
std : : string strDenom = " " ;
int nMaxDenoms = vecPrivateSendDenominations . size ( ) ;
2014-12-28 00:45:07 +01:00
2016-10-16 21:23:17 +02:00
if ( nDenom > = ( 1 < < nMaxDenoms ) ) {
return " out-of-bounds " ;
2014-12-28 00:45:07 +01:00
}
2016-10-16 21:23:17 +02:00
for ( int i = 0 ; i < nMaxDenoms ; + + i ) {
if ( nDenom & ( 1 < < i ) ) {
strDenom + = ( strDenom . empty ( ) ? " " : " + " ) + FormatMoney ( vecPrivateSendDenominations [ i ] ) ;
}
2014-12-28 00:45:07 +01:00
}
2016-10-16 21:23:17 +02:00
if ( strDenom . empty ( ) ) {
return " non-denom " ;
2014-12-28 00:45:07 +01:00
}
2016-08-05 21:49:45 +02:00
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
2016-10-16 21:23:17 +02:00
Function returns as follows ( for 4 denominations ) :
2016-08-05 21:49:45 +02:00
( 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 ) {
2016-11-20 07:52:45 +01:00
int bit = ( fSingleRandomDenom ? GetRandInt ( 2 ) : 1 ) & s . second ;
2016-08-05 21:49:45 +02:00
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-10-16 21:23:17 +02:00
bool CDarksendPool : : GetDenominationsBits ( int nDenom , std : : vector < int > & vecBitsRet )
{
// ( bit on if present, 4 denominations example )
2024-02-05 23:00:22 +01:00
// bit 0 - 100NBY+1
// bit 1 - 10NBY+1
// bit 2 - 1NBY+1
// bit 3 - .1NBY+1
2016-10-16 21:23:17 +02:00
int nMaxDenoms = vecPrivateSendDenominations . size ( ) ;
if ( nDenom > = ( 1 < < nMaxDenoms ) ) return false ;
vecBitsRet . clear ( ) ;
for ( int i = 0 ; i < nMaxDenoms ; + + i ) {
if ( nDenom & ( 1 < < i ) ) {
vecBitsRet . push_back ( i ) ;
}
}
return ! vecBitsRet . empty ( ) ;
}
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-10-20 23:11:57 +02:00
std : : string CDarksendPool : : GetMessageByID ( PoolMessage nMessageID )
2016-08-05 21:49:45 +02:00
{
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. " ) ;
2016-10-20 23:11:57 +02:00
case MSG_NOERR : return _ ( " No errors detected. " ) ;
2016-08-05 21:49:45 +02:00
case MSG_SUCCESS : return _ ( " Transaction created successfully. " ) ;
case MSG_ENTRIES_ADDED : return _ ( " Your entries added successfully. " ) ;
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-11-02 16:54:36 +01:00
return CheckSignature ( activeMasternode . pubKeyMasternode ) ;
2016-08-05 21:49:45 +02:00
}
2016-11-02 16:54:36 +01:00
bool CDarksendQueue : : CheckSignature ( const CPubKey & pubKeyMasternode )
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 ) ;
2016-08-12 07:43:18 +02:00
std : : string strError = " " ;
2016-08-05 21:49:45 +02:00
2016-11-02 16:54:36 +01:00
if ( ! darkSendSigner . VerifyMessage ( pubKeyMasternode , 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 : : Relay ( )
{
2024-12-16 19:40:22 +01:00
std : : vector < CNode * > vNodesCopy ;
{
LOCK ( cs_vNodes ) ;
vNodesCopy = vNodes ;
BOOST_FOREACH ( CNode * pnode , vNodesCopy )
pnode - > AddRef ( ) ;
}
2016-11-28 15:21:50 +01:00
BOOST_FOREACH ( CNode * pnode , vNodesCopy )
2016-10-13 11:45:30 +02:00
if ( pnode - > nVersion > = MIN_PRIVATESEND_PEER_PROTO_VERSION )
pnode - > PushMessage ( NetMsgType : : DSQUEUE , ( * this ) ) ;
2014-12-09 02:17:57 +01:00
2024-12-16 19:40:22 +01:00
{
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodesCopy )
pnode - > Release ( ) ;
}
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-11-02 16:54:36 +01:00
return CheckSignature ( activeMasternode . pubKeyMasternode ) ;
2014-12-09 02:17:57 +01:00
}
2016-11-02 16:54:36 +01:00
bool CDarksendBroadcastTx : : CheckSignature ( const CPubKey & pubKeyMasternode )
2016-08-05 21:49:45 +02:00
{
std : : string strMessage = tx . GetHash ( ) . ToString ( ) + boost : : lexical_cast < std : : string > ( sigTime ) ;
std : : string strError = " " ;
2016-11-02 16:54:36 +01:00
if ( ! darkSendSigner . VerifyMessage ( pubKeyMasternode , 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-10-13 11:45:30 +02:00
if ( pnode - > nVersion > = MIN_PRIVATESEND_PEER_PROTO_VERSION )
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-10-20 23:11:57 +02:00
void CDarksendPool : : PushStatus ( CNode * pnode , PoolStatusUpdate nStatusUpdate , PoolMessage nMessageID )
{
if ( ! pnode ) return ;
2016-11-07 00:27:10 +01:00
pnode - > PushMessage ( NetMsgType : : DSSTATUSUPDATE , nSessionID , ( int ) nState , ( int ) vecEntries . size ( ) , ( int ) nStatusUpdate , ( int ) nMessageID ) ;
2016-10-20 23:11:57 +02:00
}
void CDarksendPool : : RelayStatus ( PoolStatusUpdate nStatusUpdate , PoolMessage nMessageID )
2015-03-02 00:09:33 +01:00
{
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodes )
2016-10-13 11:45:30 +02:00
if ( pnode - > nVersion > = MIN_PRIVATESEND_PEER_PROTO_VERSION )
2016-10-20 23:11:57 +02:00
PushStatus ( pnode , nStatusUpdate , nMessageID ) ;
2015-03-02 00:09:33 +01:00
}
2016-11-07 00:27:51 +01:00
void CDarksendPool : : RelayCompletedTransaction ( PoolMessage nMessageID )
2015-03-02 00:09:33 +01:00
{
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodes )
2016-10-13 11:45:30 +02:00
if ( pnode - > nVersion > = MIN_PRIVATESEND_PEER_PROTO_VERSION )
2016-11-07 00:27:51 +01:00
pnode - > PushMessage ( NetMsgType : : DSCOMPLETE , nSessionID , ( int ) nMessageID ) ;
2016-08-05 21:49:45 +02:00
}
2016-10-20 23:11:57 +02:00
void CDarksendPool : : SetState ( PoolState nStateNew )
2016-08-05 21:49:45 +02:00
{
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 ) ;
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 ( )
{
2024-12-16 19:40:22 +01:00
if ( fLiteMode ) return ; // disable all Neobytes 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
2024-02-05 23:00:22 +01:00
RenameThread ( " neobytes-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
2017-01-01 18:48:53 +01:00
// make sure to check all masternodes first
mnodeman . Check ( ) ;
2015-07-23 04:19:37 +02:00
// check if we should activate or ping every few minutes,
2016-12-15 17:27:24 +01:00
// slightly postpone first run to give net thread a chance to connect to some peers
if ( nTick % MASTERNODE_MIN_MNP_SECONDS = = 15 )
2016-08-05 21:49:45 +02:00
activeMasternode . ManageState ( ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( nTick % 60 = = 0 ) {
2015-07-14 08:10:18 +02:00
mnodeman . ProcessMasternodeConnections ( ) ;
2017-01-01 18:48:53 +01:00
mnodeman . CheckAndRemove ( ) ;
2016-04-13 19:49:47 +02:00
mnpayments . CheckAndRemove ( ) ;
2017-01-29 09:22:14 +01:00
instantsend . CheckAndRemove ( ) ;
2015-07-14 08:10:18 +02:00
}
2017-02-17 21:08:29 +01:00
if ( fMasterNode & & ( nTick % ( 60 * 5 ) = = 0 ) ) {
mnodeman . DoFullVerificationStep ( ) ;
}
2015-07-14 08:10:18 +02:00
darkSendPool . CheckTimeout ( ) ;
darkSendPool . CheckForCompleteQueue ( ) ;
2016-08-05 21:49:45 +02:00
if ( nDoAutoNextRun = = nTick ) {
2016-10-20 23:11:57 +02:00
darkSendPool . DoAutomaticDenominating ( ) ;
2016-11-20 07:52:45 +01:00
nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetRandInt ( PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN ) ;
2015-07-14 08:10:18 +02:00
}
2014-12-09 02:17:57 +01:00
}
}
}