2017-05-05 13:26:27 +02:00
// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include "privatesend.h"
# include "activemasternode.h"
# include "consensus/validation.h"
# include "governance.h"
# include "init.h"
# include "instantx.h"
# include "masternode-payments.h"
# include "masternode-sync.h"
# include "masternodeman.h"
# include "messagesigner.h"
# include "script/sign.h"
# include "txmempool.h"
# include "util.h"
# include "utilmoneystr.h"
# include <boost/lexical_cast.hpp>
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 . fHasSig = true ;
return true ;
}
}
return false ;
}
2017-06-30 20:30:16 +02:00
bool CDarksendQueue : : Sign ( )
2017-05-05 13:26:27 +02:00
{
2017-06-30 20:30:16 +02:00
if ( ! fMasterNode ) return false ;
2017-05-05 13:26:27 +02:00
2017-06-30 20:30:16 +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 ) ;
2017-05-05 13:26:27 +02:00
2017-06-30 20:30:16 +02:00
if ( ! CMessageSigner : : SignMessage ( strMessage , vchSig , activeMasternode . keyMasternode ) ) {
LogPrintf ( " CDarksendQueue::Sign -- SignMessage() failed, %s \n " , ToString ( ) ) ;
return false ;
}
return CheckSignature ( activeMasternode . pubKeyMasternode ) ;
}
bool CDarksendQueue : : CheckSignature ( const CPubKey & pubKeyMasternode )
{
std : : string strMessage = vin . ToString ( ) + boost : : lexical_cast < std : : string > ( nDenom ) + boost : : lexical_cast < std : : string > ( nTime ) + boost : : lexical_cast < std : : string > ( fReady ) ;
std : : string strError = " " ;
if ( ! CMessageSigner : : VerifyMessage ( pubKeyMasternode , vchSig , strMessage , strError ) ) {
LogPrintf ( " CDarksendQueue::CheckSignature -- Got bad Masternode queue signature: %s; error: %s \n " , ToString ( ) , strError ) ;
return false ;
}
return true ;
}
2017-09-19 16:51:38 +02:00
bool CDarksendQueue : : Relay ( CConnman & connman )
2017-06-30 20:30:16 +02:00
{
2017-09-19 16:51:38 +02:00
std : : vector < CNode * > vNodesCopy = connman . CopyNodeVector ( ) ;
2017-06-30 20:30:16 +02:00
BOOST_FOREACH ( CNode * pnode , vNodesCopy )
if ( pnode - > nVersion > = MIN_PRIVATESEND_PEER_PROTO_VERSION )
2017-09-19 16:51:38 +02:00
connman . PushMessage ( pnode , NetMsgType : : DSQUEUE , ( * this ) ) ;
2017-06-30 20:30:16 +02:00
2017-09-19 16:51:38 +02:00
connman . ReleaseNodeVector ( vNodesCopy ) ;
2017-06-30 20:30:16 +02:00
return true ;
2017-05-05 13:26:27 +02:00
}
2017-06-30 20:30:16 +02:00
bool CDarksendBroadcastTx : : Sign ( )
{
if ( ! fMasterNode ) return false ;
std : : string strMessage = tx . GetHash ( ) . ToString ( ) + boost : : lexical_cast < std : : string > ( sigTime ) ;
if ( ! CMessageSigner : : SignMessage ( strMessage , vchSig , activeMasternode . keyMasternode ) ) {
LogPrintf ( " CDarksendBroadcastTx::Sign -- SignMessage() failed \n " ) ;
return false ;
}
return CheckSignature ( activeMasternode . pubKeyMasternode ) ;
}
bool CDarksendBroadcastTx : : CheckSignature ( const CPubKey & pubKeyMasternode )
{
std : : string strMessage = tx . GetHash ( ) . ToString ( ) + boost : : lexical_cast < std : : string > ( sigTime ) ;
std : : string strError = " " ;
if ( ! CMessageSigner : : VerifyMessage ( pubKeyMasternode , vchSig , strMessage , strError ) ) {
LogPrintf ( " CDarksendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s \n " , strError ) ;
return false ;
}
return true ;
}
2017-07-10 16:42:09 +02:00
bool CDarksendBroadcastTx : : IsExpired ( int nHeight )
{
// expire confirmed DSTXes after ~1h since confirmation
return ( nConfirmedHeight ! = - 1 ) & & ( nHeight - nConfirmedHeight > 24 ) ;
}
2017-06-30 20:30:16 +02:00
void CPrivateSendBase : : SetNull ( )
2017-05-05 13:26:27 +02:00
{
// Both sides
nState = POOL_STATE_IDLE ;
nSessionID = 0 ;
nSessionDenom = 0 ;
vecEntries . clear ( ) ;
finalMutableTransaction . vin . clear ( ) ;
finalMutableTransaction . vout . clear ( ) ;
nTimeLastSuccessfulStep = GetTimeMillis ( ) ;
}
2017-12-04 07:06:07 +01:00
void CPrivateSendBase : : CheckQueue ( )
{
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 " , " CPrivateSendBase::%s -- Removing expired queue (%s) \n " , __func__ , ( * it ) . ToString ( ) ) ;
it = vecDarksendQueue . erase ( it ) ;
} else + + it ;
}
}
2017-06-30 20:30:16 +02:00
std : : string CPrivateSendBase : : GetStateString ( ) const
2017-05-05 13:26:27 +02:00
{
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 " ;
}
}
2017-06-30 20:30:16 +02:00
// Definitions for static data members
std : : vector < CAmount > CPrivateSend : : vecStandardDenominations ;
std : : map < uint256 , CDarksendBroadcastTx > CPrivateSend : : mapDSTX ;
CCriticalSection CPrivateSend : : cs_mapdstx ;
void CPrivateSend : : InitStandardDenominations ( )
{
vecStandardDenominations . 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
*/
/* Disabled
vecStandardDenominations . push_back ( ( 100 * COIN ) + 100000 ) ;
*/
vecStandardDenominations . push_back ( ( 10 * COIN ) + 10000 ) ;
vecStandardDenominations . push_back ( ( 1 * COIN ) + 1000 ) ;
vecStandardDenominations . push_back ( ( .1 * COIN ) + 100 ) ;
vecStandardDenominations . push_back ( ( .01 * COIN ) + 10 ) ;
/* Disabled till we need them
vecStandardDenominations . push_back ( ( .001 * COIN ) + 1 ) ;
*/
}
2017-05-05 13:26:27 +02:00
// check to make sure the collateral provided by the client is valid
bool CPrivateSend : : IsCollateralValid ( const CTransaction & txCollateral )
{
if ( txCollateral . vout . empty ( ) ) return false ;
if ( txCollateral . nLockTime ! = 0 ) return false ;
CAmount nValueIn = 0 ;
CAmount nValueOut = 0 ;
BOOST_FOREACH ( const CTxOut txout , txCollateral . vout ) {
nValueOut + = txout . nValue ;
2017-12-07 02:33:51 +01:00
if ( ! txout . scriptPubKey . IsPayToPublicKeyHash ( ) ) {
2017-05-05 13:26:27 +02:00
LogPrintf ( " CPrivateSend::IsCollateralValid -- Invalid Script, txCollateral=%s " , txCollateral . ToString ( ) ) ;
return false ;
}
}
BOOST_FOREACH ( const CTxIn txin , txCollateral . vin ) {
2017-09-26 16:33:46 +02:00
Coin coin ;
if ( ! GetUTXOCoin ( txin . prevout , coin ) ) {
2017-08-25 14:56:48 +02:00
LogPrint ( " privatesend " , " CPrivateSend::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s " , txCollateral . ToString ( ) ) ;
return false ;
2017-05-05 13:26:27 +02:00
}
2017-09-26 16:33:46 +02:00
nValueIn + = coin . out . nValue ;
2017-05-05 13:26:27 +02:00
}
2017-06-30 20:30:16 +02:00
//collateral transactions are required to pay out a small fee to the miners
if ( nValueIn - nValueOut < GetCollateralAmount ( ) ) {
2017-05-05 13:26:27 +02:00
LogPrint ( " privatesend " , " CPrivateSend::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s " , nValueOut - nValueIn , txCollateral . ToString ( ) ) ;
return false ;
}
LogPrint ( " privatesend " , " CPrivateSend::IsCollateralValid -- %s " , txCollateral . ToString ( ) ) ;
{
LOCK ( cs_main ) ;
CValidationState validationState ;
if ( ! AcceptToMemoryPool ( mempool , validationState , txCollateral , false , NULL , false , true , true ) ) {
LogPrint ( " privatesend " , " CPrivateSend::IsCollateralValid -- didn't pass AcceptToMemoryPool() \n " ) ;
return false ;
}
}
return true ;
}
2017-12-04 07:06:07 +01:00
bool CPrivateSend : : IsCollateralAmount ( CAmount nInputAmount )
{
// collateral inputs should always be a 2x..4x of mixing collateral
return nInputAmount > GetCollateralAmount ( ) & &
nInputAmount < = GetMaxCollateralAmount ( ) & &
nInputAmount % GetCollateralAmount ( ) = = 0 ;
}
2017-05-05 13:26:27 +02:00
/* Create a nice string to show the denominations
Function returns as follows ( for 4 denominations ) :
( bit on if present )
bit 0 - 100
bit 1 - 10
bit 2 - 1
bit 3 - .1
bit 4 and so on - out - of - bounds
none of above - non - denom
*/
std : : string CPrivateSend : : GetDenominationsToString ( int nDenom )
{
std : : string strDenom = " " ;
2017-06-30 20:30:16 +02:00
int nMaxDenoms = vecStandardDenominations . size ( ) ;
2017-05-05 13:26:27 +02:00
if ( nDenom > = ( 1 < < nMaxDenoms ) ) {
return " out-of-bounds " ;
}
for ( int i = 0 ; i < nMaxDenoms ; + + i ) {
if ( nDenom & ( 1 < < i ) ) {
2017-06-30 20:30:16 +02:00
strDenom + = ( strDenom . empty ( ) ? " " : " + " ) + FormatMoney ( vecStandardDenominations [ i ] ) ;
2017-05-05 13:26:27 +02:00
}
}
if ( strDenom . empty ( ) ) {
return " non-denom " ;
}
return strDenom ;
}
/* Return a bitshifted integer representing the denominations in this list
Function returns as follows ( for 4 denominations ) :
( bit on if present )
100 - bit 0
10 - bit 1
1 - bit 2
.1 - bit 3
non - denom - 0 , all bits off
*/
int CPrivateSend : : GetDenominations ( const std : : vector < CTxOut > & vecTxOut , bool fSingleRandomDenom )
{
std : : vector < std : : pair < CAmount , int > > vecDenomUsed ;
// make a list of denominations, with zero uses
2017-06-30 20:30:16 +02:00
BOOST_FOREACH ( CAmount nDenomValue , vecStandardDenominations )
2017-05-05 13:26:27 +02:00
vecDenomUsed . push_back ( std : : make_pair ( nDenomValue , 0 ) ) ;
// look for denominations and update uses to 1
BOOST_FOREACH ( CTxOut txout , vecTxOut ) {
bool found = false ;
BOOST_FOREACH ( PAIRTYPE ( CAmount , int ) & s , vecDenomUsed ) {
if ( txout . nValue = = s . first ) {
s . second = 1 ;
found = true ;
}
}
if ( ! found ) return 0 ;
}
int nDenom = 0 ;
int c = 0 ;
// if the denomination is used, shift the bit on
BOOST_FOREACH ( PAIRTYPE ( CAmount , int ) & s , vecDenomUsed ) {
int bit = ( fSingleRandomDenom ? GetRandInt ( 2 ) : 1 ) & s . second ;
nDenom | = bit < < c + + ;
if ( fSingleRandomDenom & & bit ) break ; // use just one random denomination
}
return nDenom ;
}
bool CPrivateSend : : GetDenominationsBits ( int nDenom , std : : vector < int > & vecBitsRet )
{
// ( bit on if present, 4 denominations example )
// bit 0 - 100DASH+1
// bit 1 - 10DASH+1
// bit 2 - 1DASH+1
// bit 3 - .1DASH+1
2017-06-30 20:30:16 +02:00
int nMaxDenoms = vecStandardDenominations . size ( ) ;
2017-05-05 13:26:27 +02:00
if ( nDenom > = ( 1 < < nMaxDenoms ) ) return false ;
vecBitsRet . clear ( ) ;
for ( int i = 0 ; i < nMaxDenoms ; + + i ) {
if ( nDenom & ( 1 < < i ) ) {
vecBitsRet . push_back ( i ) ;
}
}
return ! vecBitsRet . empty ( ) ;
}
int CPrivateSend : : GetDenominationsByAmounts ( const std : : vector < CAmount > & vecAmount )
{
CScript scriptTmp = CScript ( ) ;
std : : vector < CTxOut > vecTxOut ;
BOOST_REVERSE_FOREACH ( CAmount nAmount , vecAmount ) {
CTxOut txout ( nAmount , scriptTmp ) ;
vecTxOut . push_back ( txout ) ;
}
return GetDenominations ( vecTxOut , true ) ;
}
2017-12-04 07:06:07 +01:00
bool CPrivateSend : : IsDenominatedAmount ( CAmount nInputAmount )
{
for ( const auto & nDenomValue : vecStandardDenominations )
if ( nInputAmount = = nDenomValue )
return true ;
return false ;
}
2017-05-05 13:26:27 +02:00
std : : string CPrivateSend : : GetMessageByID ( PoolMessage nMessageID )
{
switch ( nMessageID ) {
case ERR_ALREADY_HAVE : return _ ( " Already have that input. " ) ;
case ERR_DENOM : return _ ( " No matching denominations found for mixing. " ) ;
case ERR_ENTRIES_FULL : return _ ( " Entries are full. " ) ;
case ERR_EXISTING_TX : return _ ( " Not compatible with existing transactions. " ) ;
case ERR_FEES : return _ ( " Transaction fees are too high. " ) ;
case ERR_INVALID_COLLATERAL : return _ ( " Collateral not valid. " ) ;
case ERR_INVALID_INPUT : return _ ( " Input is not valid. " ) ;
case ERR_INVALID_SCRIPT : return _ ( " Invalid script detected. " ) ;
case ERR_INVALID_TX : return _ ( " Transaction not valid. " ) ;
case ERR_MAXIMUM : return _ ( " Entry exceeds maximum size. " ) ;
case ERR_MN_LIST : return _ ( " Not in the Masternode list. " ) ;
case ERR_MODE : return _ ( " Incompatible mode. " ) ;
case ERR_NON_STANDARD_PUBKEY : return _ ( " Non-standard public key detected. " ) ;
case ERR_NOT_A_MN : return _ ( " This is not a Masternode. " ) ; // not used
case ERR_QUEUE_FULL : return _ ( " Masternode queue is full. " ) ;
case ERR_RECENT : return _ ( " Last PrivateSend was too recent. " ) ;
case ERR_SESSION : return _ ( " Session not complete! " ) ;
case ERR_MISSING_TX : return _ ( " Missing input transaction information. " ) ;
case ERR_VERSION : return _ ( " Incompatible version. " ) ;
case MSG_NOERR : return _ ( " No errors detected. " ) ;
case MSG_SUCCESS : return _ ( " Transaction created successfully. " ) ;
case MSG_ENTRIES_ADDED : return _ ( " Your entries added successfully. " ) ;
default : return _ ( " Unknown response. " ) ;
}
}
2017-06-30 20:30:16 +02:00
void CPrivateSend : : AddDSTX ( const CDarksendBroadcastTx & dstx )
2017-05-05 13:26:27 +02:00
{
2017-06-30 20:30:16 +02:00
LOCK ( cs_mapdstx ) ;
mapDSTX . insert ( std : : make_pair ( dstx . tx . GetHash ( ) , dstx ) ) ;
2017-05-05 13:26:27 +02:00
}
2017-06-30 20:30:16 +02:00
CDarksendBroadcastTx CPrivateSend : : GetDSTX ( const uint256 & hash )
2017-05-05 13:26:27 +02:00
{
2017-06-30 20:30:16 +02:00
LOCK ( cs_mapdstx ) ;
auto it = mapDSTX . find ( hash ) ;
return ( it = = mapDSTX . end ( ) ) ? CDarksendBroadcastTx ( ) : it - > second ;
2017-05-05 13:26:27 +02:00
}
2017-07-10 16:42:09 +02:00
void CPrivateSend : : CheckDSTXes ( int nHeight )
{
LOCK ( cs_mapdstx ) ;
std : : map < uint256 , CDarksendBroadcastTx > : : iterator it = mapDSTX . begin ( ) ;
while ( it ! = mapDSTX . end ( ) ) {
if ( it - > second . IsExpired ( nHeight ) ) {
mapDSTX . erase ( it + + ) ;
} else {
+ + it ;
}
}
LogPrint ( " privatesend " , " CPrivateSend::CheckDSTXes -- mapDSTX.size()=%llu \n " , mapDSTX . size ( ) ) ;
}
2017-12-07 10:42:47 +01:00
void CPrivateSend : : UpdatedBlockTip ( const CBlockIndex * pindex )
{
if ( pindex & & ! fLiteMode & & masternodeSync . IsMasternodeListSynced ( ) ) {
CheckDSTXes ( pindex - > nHeight ) ;
}
}
2017-07-10 16:42:09 +02:00
void CPrivateSend : : SyncTransaction ( const CTransaction & tx , const CBlock * pblock )
{
if ( tx . IsCoinBase ( ) ) return ;
LOCK2 ( cs_main , cs_mapdstx ) ;
uint256 txHash = tx . GetHash ( ) ;
if ( ! mapDSTX . count ( txHash ) ) return ;
// When tx is 0-confirmed or conflicted, pblock is NULL and nConfirmedHeight should be set to -1
CBlockIndex * pblockindex = NULL ;
if ( pblock ) {
uint256 blockHash = pblock - > GetHash ( ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( blockHash ) ;
if ( mi = = mapBlockIndex . end ( ) | | ! mi - > second ) {
// shouldn't happen
LogPrint ( " privatesend " , " CPrivateSendClient::SyncTransaction -- Failed to find block %s \n " , blockHash . ToString ( ) ) ;
return ;
}
pblockindex = mi - > second ;
}
mapDSTX [ txHash ] . SetConfirmedHeight ( pblockindex ? pblockindex - > nHeight : - 1 ) ;
LogPrint ( " privatesend " , " CPrivateSendClient::SyncTransaction -- txid=%s \n " , txHash . ToString ( ) ) ;
}
2017-05-05 13:26:27 +02:00
//TODO: Rename/move to core
2017-09-19 16:51:38 +02:00
void ThreadCheckPrivateSend ( CConnman & connman )
2017-05-05 13:26:27 +02:00
{
if ( fLiteMode ) return ; // disable all Dash specific functionality
static bool fOneThread ;
if ( fOneThread ) return ;
fOneThread = true ;
// Make this thread recognisable as the PrivateSend thread
RenameThread ( " dash-ps " ) ;
unsigned int nTick = 0 ;
while ( true )
{
MilliSleep ( 1000 ) ;
// try to sync from all available nodes, one step at a time
2017-09-19 16:51:38 +02:00
masternodeSync . ProcessTick ( connman ) ;
2017-05-05 13:26:27 +02:00
if ( masternodeSync . IsBlockchainSynced ( ) & & ! ShutdownRequested ( ) ) {
nTick + + ;
// make sure to check all masternodes first
mnodeman . Check ( ) ;
// check if we should activate or ping every few minutes,
// slightly postpone first run to give net thread a chance to connect to some peers
if ( nTick % MASTERNODE_MIN_MNP_SECONDS = = 15 )
2017-09-19 16:51:38 +02:00
activeMasternode . ManageState ( connman ) ;
2017-05-05 13:26:27 +02:00
if ( nTick % 60 = = 0 ) {
2017-09-19 16:51:38 +02:00
mnodeman . ProcessMasternodeConnections ( connman ) ;
mnodeman . CheckAndRemove ( connman ) ;
2017-05-05 13:26:27 +02:00
mnpayments . CheckAndRemove ( ) ;
instantsend . CheckAndRemove ( ) ;
}
if ( fMasterNode & & ( nTick % ( 60 * 5 ) = = 0 ) ) {
2017-09-19 16:51:38 +02:00
mnodeman . DoFullVerificationStep ( connman ) ;
2017-05-05 13:26:27 +02:00
}
if ( nTick % ( 60 * 5 ) = = 0 ) {
2017-09-19 16:51:38 +02:00
governance . DoMaintenance ( connman ) ;
2017-05-05 13:26:27 +02:00
}
}
}
}