2016-02-02 16:28:56 +01:00
// Copyright (c) 2014-2016 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.
2014-12-09 02:17:57 +01:00
2016-02-02 16:28:56 +01:00
# include "consensus/validation.h"
2014-12-09 02:17:57 +01:00
# include "sync.h"
# include "net.h"
# include "key.h"
# include "util.h"
# include "base58.h"
# include "protocol.h"
# include "instantx.h"
# include "activemasternode.h"
# include "darksend.h"
2016-01-24 20:05:31 +01:00
# include "masternode-sync.h"
# include "masternodeman.h"
2015-02-09 20:28:29 +01:00
# include "spork.h"
2016-03-24 16:54:29 +01:00
# include <boost/algorithm/string/replace.hpp>
2014-12-09 02:17:57 +01:00
# include <boost/lexical_cast.hpp>
2016-03-24 16:54:29 +01:00
# include <boost/thread.hpp>
2014-12-09 02:17:57 +01:00
using namespace std ;
using namespace boost ;
2014-12-31 03:54:00 +01:00
2014-12-09 02:17:57 +01:00
std : : map < uint256 , CTransaction > mapTxLockReq ;
std : : map < uint256 , CTransaction > mapTxLockReqRejected ;
2015-02-06 20:07:22 +01:00
std : : map < uint256 , CConsensusVote > mapTxLockVote ;
2014-12-09 02:17:57 +01:00
std : : map < uint256 , CTransactionLock > mapTxLocks ;
2015-02-04 21:20:13 +01:00
std : : map < COutPoint , uint256 > mapLockedInputs ;
2015-02-02 18:33:52 +01:00
std : : map < uint256 , int64_t > mapUnknownVotes ; //track votes with no tx for DOS
2015-02-01 21:04:20 +01:00
int nCompleteTXLocks ;
2014-12-09 02:17:57 +01:00
//txlock - Locks transaction
//
//step 1.) Broadcast intention to lock transaction inputs, "txlreg", CTransaction
2015-06-04 20:54:33 +02:00
//step 2.) Top INSTANTX_SIGNATURES_TOTAL masternodes, open connect to top 1 masternode.
// Send "txvote", CTransaction, Signature, Approve
//step 3.) Top 1 masternode, waits for INSTANTX_SIGNATURES_REQUIRED messages. Upon success, sends "txlock'
2014-12-09 02:17:57 +01:00
void ProcessMessageInstantX ( CNode * pfrom , std : : string & strCommand , CDataStream & vRecv )
{
2015-01-18 16:28:16 +01:00
if ( fLiteMode ) return ; //disable all darksend/masternode related functionality
2016-07-30 13:04:27 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTX ) ) return ;
2015-08-07 06:48:55 +02:00
if ( ! masternodeSync . IsBlockchainSynced ( ) ) return ;
2014-12-09 02:17:57 +01:00
2016-02-17 23:18:57 +01:00
if ( strCommand = = NetMsgType : : IX )
2014-12-09 02:17:57 +01:00
{
2015-07-10 00:08:26 +02:00
//LogPrintf("ProcessMessageInstantX::ix\n");
2014-12-09 02:17:57 +01:00
CDataStream vMsg ( vRecv ) ;
CTransaction tx ;
vRecv > > tx ;
2016-03-24 16:54:29 +01:00
// FIXME: this part of simulating inv is not good actually, leaving it only for 12.1 backwards compatibility
// and since we are using invs for relaying even initial ix request, this can (and should) be safely removed in 12.2
2014-12-09 02:17:57 +01:00
CInv inv ( MSG_TXLOCK_REQUEST , tx . GetHash ( ) ) ;
pfrom - > AddInventoryKnown ( inv ) ;
2016-03-24 16:54:29 +01:00
GetMainSignals ( ) . Inventory ( inv . hash ) ;
2014-12-09 02:17:57 +01:00
2016-03-21 21:23:45 +01:00
// have we seen it already?
if ( mapTxLockReq . count ( inv . hash ) | | mapTxLockReqRejected . count ( inv . hash ) ) return ;
// is it a valid one?
if ( ! IsIXTXValid ( tx ) ) return ;
2015-02-04 11:44:41 +01:00
2014-12-09 02:17:57 +01:00
BOOST_FOREACH ( const CTxOut o , tx . vout ) {
2015-07-12 23:02:39 +02:00
// IX supports normal scripts and unspendable scripts (used in DS collateral and Budget collateral).
// TODO: Look into other script types that are normal and can be included
if ( ! o . scriptPubKey . IsNormalPaymentScript ( ) & & ! o . scriptPubKey . IsUnspendable ( ) ) {
2016-06-27 17:25:22 +02:00
LogPrintf ( " ProcessMessageInstantX::ix - Invalid Script %s " , tx . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return ;
}
}
2015-02-04 22:59:19 +01:00
int nBlockHeight = CreateNewLock ( tx ) ;
2014-12-09 02:17:57 +01:00
bool fMissingInputs = false ;
CValidationState state ;
2014-12-31 03:54:00 +01:00
2015-08-05 00:49:34 +02:00
bool fAccepted = false ;
2014-12-09 02:17:57 +01:00
{
2015-08-05 00:49:34 +02:00
LOCK ( cs_main ) ;
fAccepted = AcceptToMemoryPool ( mempool , state , tx , true , & fMissingInputs ) ;
}
if ( fAccepted )
{
RelayInv ( inv ) ;
2015-02-06 20:07:22 +01:00
2015-02-04 22:59:19 +01:00
DoConsensusVote ( tx , nBlockHeight ) ;
2014-12-09 02:17:57 +01:00
2015-02-02 15:36:00 +01:00
mapTxLockReq . insert ( make_pair ( tx . GetHash ( ) , tx ) ) ;
2014-12-09 02:17:57 +01:00
2015-07-10 00:08:26 +02:00
LogPrintf ( " ProcessMessageInstantX::ix - Transaction Lock Request: %s %s : accepted %s \n " ,
2016-03-04 06:58:53 +01:00
pfrom - > addr . ToString ( ) , pfrom - > cleanSubVer ,
tx . GetHash ( ) . ToString ( )
2014-12-09 02:17:57 +01:00
) ;
2016-03-24 16:54:29 +01:00
// Masternodes will sometimes propagate votes before the transaction is known to the client.
// If this just happened - update transaction status, try forcing external script notification,
// lock inputs and resolve conflicting locks
if ( IsLockedIXTransaction ( tx . GetHash ( ) ) ) {
UpdateLockedTransaction ( tx , true ) ;
LockTransactionInputs ( tx ) ;
ResolveConflicts ( tx ) ;
}
2014-12-09 02:17:57 +01:00
return ;
} else {
2015-02-03 18:17:30 +01:00
mapTxLockReqRejected . insert ( make_pair ( tx . GetHash ( ) , tx ) ) ;
2014-12-09 02:17:57 +01:00
// can we get the conflicting transaction as proof?
2015-07-10 00:08:26 +02:00
LogPrintf ( " ProcessMessageInstantX::ix - Transaction Lock Request: %s %s : rejected %s \n " ,
2016-03-04 06:58:53 +01:00
pfrom - > addr . ToString ( ) , pfrom - > cleanSubVer ,
tx . GetHash ( ) . ToString ( )
2014-12-09 02:17:57 +01:00
) ;
2016-03-24 16:54:29 +01:00
LockTransactionInputs ( tx ) ;
ResolveConflicts ( tx ) ;
2015-02-03 18:17:30 +01:00
2014-12-09 02:17:57 +01:00
return ;
}
2014-12-31 03:54:00 +01:00
}
2016-02-17 23:18:57 +01:00
else if ( strCommand = = NetMsgType : : IXLOCKVOTE ) //InstantX Lock Consensus Votes
2014-12-09 02:17:57 +01:00
{
CConsensusVote ctx ;
vRecv > > ctx ;
2015-02-01 16:53:49 +01:00
CInv inv ( MSG_TXLOCK_VOTE , ctx . GetHash ( ) ) ;
2014-12-09 02:17:57 +01:00
pfrom - > AddInventoryKnown ( inv ) ;
2015-02-01 21:37:20 +01:00
if ( mapTxLockVote . count ( ctx . GetHash ( ) ) ) {
2015-02-01 16:53:49 +01:00
return ;
}
2014-12-09 02:17:57 +01:00
2015-02-06 20:07:22 +01:00
mapTxLockVote . insert ( make_pair ( ctx . GetHash ( ) , ctx ) ) ;
2014-12-31 03:54:00 +01:00
2015-08-07 05:07:40 +02:00
if ( ProcessConsensusVote ( pfrom , ctx ) ) {
2015-02-02 15:36:00 +01:00
//Spam/Dos protection
2015-02-02 18:33:52 +01:00
/*
Masternodes will sometimes propagate votes before the transaction is known to the client .
This tracks those messages and allows it at the same rate of the rest of the network , if
a peer violates it , it will simply be ignored
*/
2015-02-02 15:36:00 +01:00
if ( ! mapTxLockReq . count ( ctx . txHash ) & & ! mapTxLockReqRejected . count ( ctx . txHash ) ) {
if ( ! mapUnknownVotes . count ( ctx . vinMasternode . prevout . hash ) ) {
mapUnknownVotes [ ctx . vinMasternode . prevout . hash ] = GetTime ( ) + ( 60 * 10 ) ;
}
2015-02-02 18:33:52 +01:00
if ( mapUnknownVotes [ ctx . vinMasternode . prevout . hash ] > GetTime ( ) & &
mapUnknownVotes [ ctx . vinMasternode . prevout . hash ] - GetAverageVoteTime ( ) > 60 * 10 ) {
2015-07-10 00:08:26 +02:00
LogPrintf ( " ProcessMessageInstantX::ix - masternode is spamming transaction votes: %s %s \n " ,
2016-03-04 06:58:53 +01:00
ctx . vinMasternode . ToString ( ) ,
ctx . txHash . ToString ( )
2015-02-02 18:33:52 +01:00
) ;
return ;
2015-02-02 15:36:00 +01:00
} else {
mapUnknownVotes [ ctx . vinMasternode . prevout . hash ] = GetTime ( ) + ( 60 * 10 ) ;
}
}
2015-08-05 00:49:34 +02:00
RelayInv ( inv ) ;
2014-12-09 02:17:57 +01:00
}
2015-02-01 21:37:20 +01:00
2015-02-01 16:53:49 +01:00
return ;
2014-12-09 02:17:57 +01:00
}
}
2015-02-04 11:44:41 +01:00
bool IsIXTXValid ( const CTransaction & txCollateral ) {
if ( txCollateral . vout . size ( ) < 1 ) return false ;
2016-03-21 21:23:45 +01:00
if ( ! CheckFinalTx ( txCollateral ) ) {
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " IsIXTXValid - Transaction is not final - %s \n " , txCollateral . ToString ( ) ) ;
2016-03-21 21:23:45 +01:00
return false ;
}
2015-02-04 11:44:41 +01:00
int64_t nValueIn = 0 ;
int64_t nValueOut = 0 ;
bool missingTx = false ;
BOOST_FOREACH ( const CTxOut o , txCollateral . vout )
nValueOut + = o . nValue ;
BOOST_FOREACH ( const CTxIn i , txCollateral . vin ) {
CTransaction tx2 ;
uint256 hash ;
2016-02-02 16:28:56 +01:00
if ( GetTransaction ( i . prevout . hash , tx2 , Params ( ) . GetConsensus ( ) , hash , true ) ) {
2015-02-04 11:44:41 +01:00
if ( tx2 . vout . size ( ) > i . prevout . n ) {
nValueIn + = tx2 . vout [ i . prevout . n ] . nValue ;
}
} else {
missingTx = true ;
}
}
2016-07-30 13:04:27 +02:00
if ( nValueOut > sporkManager . GetSporkValue ( SPORK_5_MAX_VALUE ) * COIN ) {
LogPrint ( " instantsend " , " IsIXTXValid -- Transaction value too high: nValueOut=%d, txCollateral=%s " , nValueOut , txCollateral . ToString ( ) ) ;
2015-02-11 15:47:21 +01:00
return false ;
}
2015-02-04 11:44:41 +01:00
if ( missingTx ) {
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " IsIXTXValid - Unknown inputs in IX transaction - %s \n " , txCollateral . ToString ( ) ) ;
2015-02-08 04:04:08 +01:00
/*
This happens sometimes for an unknown reason , so we ' ll return that it ' s a valid transaction .
If someone submits an invalid transaction it will be rejected by the network anyway and this isn ' t
very common , but we don ' t want to block IX just because the client can ' t figure out the fee .
*/
return true ;
2015-02-04 11:44:41 +01:00
}
2016-05-30 08:22:15 +02:00
if ( nValueIn - nValueOut < INSTANTSEND_MIN_FEE ) {
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " IsIXTXValid - did not include enough fees in transaction %d \n %s \n " , nValueOut - nValueIn , txCollateral . ToString ( ) ) ;
2015-02-04 11:44:41 +01:00
return false ;
}
return true ;
}
2015-02-04 22:59:19 +01:00
int64_t CreateNewLock ( CTransaction tx )
2014-12-09 02:17:57 +01:00
{
2015-02-04 22:59:19 +01:00
int64_t nTxAge = 0 ;
BOOST_REVERSE_FOREACH ( CTxIn i , tx . vin ) {
nTxAge = GetInputAge ( i ) ;
2015-08-04 19:42:05 +02:00
if ( nTxAge < 5 ) //1 less than the "send IX" gui requires, incase of a block propagating the network at the time
2015-02-04 22:59:19 +01:00
{
2016-03-04 06:58:53 +01:00
LogPrintf ( " CreateNewLock - Transaction not found / too new: %d / %s \n " , nTxAge , tx . GetHash ( ) . ToString ( ) ) ;
2015-02-04 22:59:19 +01:00
return 0 ;
}
}
2014-12-09 02:17:57 +01:00
2015-02-04 22:09:50 +01:00
/*
2015-02-04 22:59:19 +01:00
Use a blockheight newer than the input .
This prevents attackers from using transaction mallibility to predict which masternodes
they ' ll use .
2015-02-04 22:09:50 +01:00
*/
2016-03-02 21:57:24 +01:00
int nBlockHeight = 0 ;
{
LOCK ( cs_main ) ;
CBlockIndex * tip = chainActive . Tip ( ) ;
if ( tip ) nBlockHeight = tip - > nHeight - nTxAge + 4 ;
else return 0 ;
}
2015-02-04 22:09:50 +01:00
if ( ! mapTxLocks . count ( tx . GetHash ( ) ) ) {
2016-03-04 06:58:53 +01:00
LogPrintf ( " CreateNewLock - New Transaction Lock %s ! \n " , tx . GetHash ( ) . ToString ( ) ) ;
2015-02-04 22:09:50 +01:00
CTransactionLock newLock ;
newLock . nBlockHeight = nBlockHeight ;
2015-07-07 14:47:22 +02:00
newLock . nExpiration = GetTime ( ) + ( 60 * 60 ) ; //locks expire after 60 minutes (24 confirmations)
2015-02-05 16:52:02 +01:00
newLock . nTimeout = GetTime ( ) + ( 60 * 5 ) ;
2015-02-04 22:09:50 +01:00
newLock . txHash = tx . GetHash ( ) ;
mapTxLocks . insert ( make_pair ( tx . GetHash ( ) , newLock ) ) ;
} else {
mapTxLocks [ tx . GetHash ( ) ] . nBlockHeight = nBlockHeight ;
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " CreateNewLock - Transaction Lock Exists %s ! \n " , tx . GetHash ( ) . ToString ( ) ) ;
2015-02-04 22:09:50 +01:00
}
2015-03-13 10:28:20 +01:00
2015-02-04 22:59:19 +01:00
return nBlockHeight ;
}
// check if we need to vote on this transaction
void DoConsensusVote ( CTransaction & tx , int64_t nBlockHeight )
{
if ( ! fMasterNode ) return ;
2015-02-23 21:01:21 +01:00
int n = mnodeman . GetMasternodeRank ( activeMasternode . vin , nBlockHeight , MIN_INSTANTX_PROTO_VERSION ) ;
2015-02-06 05:41:17 +01:00
if ( n = = - 1 )
{
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::DoConsensusVote - Unknown Masternode %s \n " , activeMasternode . vin . ToString ( ) ) ;
2015-02-06 05:41:17 +01:00
return ;
}
if ( n > INSTANTX_SIGNATURES_TOTAL )
{
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::DoConsensusVote - Masternode not in the top %d (%d) \n " , INSTANTX_SIGNATURES_TOTAL , n ) ;
2015-02-06 05:41:17 +01:00
return ;
}
2015-02-04 22:59:19 +01:00
/*
nBlockHeight calculated from the transaction is the authoritive source
*/
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::DoConsensusVote - In the top %d (%d) \n " , INSTANTX_SIGNATURES_TOTAL , n ) ;
2015-02-06 05:41:17 +01:00
2014-12-09 02:17:57 +01:00
CConsensusVote ctx ;
2014-12-06 20:41:53 +01:00
ctx . vinMasternode = activeMasternode . vin ;
2015-02-02 13:24:04 +01:00
ctx . txHash = tx . GetHash ( ) ;
2014-12-31 03:54:00 +01:00
ctx . nBlockHeight = nBlockHeight ;
2014-12-09 02:17:57 +01:00
if ( ! ctx . Sign ( ) ) {
LogPrintf ( " InstantX::DoConsensusVote - Failed to sign consensus vote \n " ) ;
return ;
}
if ( ! ctx . SignatureValid ( ) ) {
LogPrintf ( " InstantX::DoConsensusVote - Signature invalid \n " ) ;
return ;
}
2015-02-02 13:01:06 +01:00
2015-02-06 20:07:22 +01:00
mapTxLockVote [ ctx . GetHash ( ) ] = ctx ;
CInv inv ( MSG_TXLOCK_VOTE , ctx . GetHash ( ) ) ;
2015-07-08 02:37:23 +02:00
RelayInv ( inv ) ;
2014-12-09 02:17:57 +01:00
}
//received a consensus vote
2015-08-07 05:07:40 +02:00
bool ProcessConsensusVote ( CNode * pnode , CConsensusVote & ctx )
2014-12-09 02:17:57 +01:00
{
2015-02-23 21:01:21 +01:00
int n = mnodeman . GetMasternodeRank ( ctx . vinMasternode , ctx . nBlockHeight , MIN_INSTANTX_PROTO_VERSION ) ;
2014-12-09 02:17:57 +01:00
2015-02-25 12:54:03 +01:00
CMasternode * pmn = mnodeman . Find ( ctx . vinMasternode ) ;
if ( pmn ! = NULL )
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::ProcessConsensusVote - Masternode ADDR %s %d \n " , pmn - > addr . ToString ( ) , n ) ;
2015-02-06 16:54:39 +01:00
2014-12-31 03:54:00 +01:00
if ( n = = - 1 )
2014-12-09 02:17:57 +01:00
{
2015-02-06 17:03:50 +01:00
//can be caused by past versions trying to vote with an invalid protocol
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::ProcessConsensusVote - Unknown Masternode %s \n " , ctx . vinMasternode . ToString ( ) ) ;
2015-08-07 05:07:40 +02:00
mnodeman . AskForMN ( pnode , ctx . vinMasternode ) ;
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2015-02-05 18:56:11 +01:00
if ( n > INSTANTX_SIGNATURES_TOTAL )
2014-12-09 02:17:57 +01:00
{
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::ProcessConsensusVote - Masternode not in the top %d (%d) - %s \n " , INSTANTX_SIGNATURES_TOTAL , n , ctx . GetHash ( ) . ToString ( ) ) ;
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
if ( ! ctx . SignatureValid ( ) ) {
LogPrintf ( " InstantX::ProcessConsensusVote - Signature invalid \n " ) ;
2015-08-07 05:07:40 +02:00
// don't ban, it could just be a non-synced masternode
mnodeman . AskForMN ( pnode , ctx . vinMasternode ) ;
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2015-02-02 13:24:04 +01:00
if ( ! mapTxLocks . count ( ctx . txHash ) ) {
2016-03-04 06:58:53 +01:00
LogPrintf ( " InstantX::ProcessConsensusVote - New Transaction Lock %s ! \n " , ctx . txHash . ToString ( ) ) ;
2015-02-01 21:04:20 +01:00
2015-02-01 16:53:49 +01:00
CTransactionLock newLock ;
2015-02-04 22:09:50 +01:00
newLock . nBlockHeight = 0 ;
2015-02-01 21:04:20 +01:00
newLock . nExpiration = GetTime ( ) + ( 60 * 60 ) ;
2015-02-05 16:52:02 +01:00
newLock . nTimeout = GetTime ( ) + ( 60 * 5 ) ;
2015-02-02 13:24:04 +01:00
newLock . txHash = ctx . txHash ;
mapTxLocks . insert ( make_pair ( ctx . txHash , newLock ) ) ;
2015-06-24 23:11:45 +02:00
} else
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::ProcessConsensusVote - Transaction Lock Exists %s ! \n " , ctx . txHash . ToString ( ) ) ;
2015-02-01 16:53:49 +01:00
2014-12-09 02:17:57 +01:00
//compile consessus vote
2015-02-02 13:24:04 +01:00
std : : map < uint256 , CTransactionLock > : : iterator i = mapTxLocks . find ( ctx . txHash ) ;
2015-02-01 16:53:49 +01:00
if ( i ! = mapTxLocks . end ( ) ) {
( * i ) . second . AddSignature ( ctx ) ;
2015-02-03 23:40:00 +01:00
2016-03-21 21:23:45 +01:00
int nSignatures = ( * i ) . second . CountSignatures ( ) ;
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::ProcessConsensusVote - Transaction Lock Votes %d - %s ! \n " , nSignatures , ctx . GetHash ( ) . ToString ( ) ) ;
2015-02-03 23:40:00 +01:00
2016-03-21 21:23:45 +01:00
if ( nSignatures > = INSTANTX_SIGNATURES_REQUIRED ) {
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::ProcessConsensusVote - Transaction Lock Is Complete %s ! \n " , ctx . txHash . ToString ( ) ) ;
2015-02-03 18:17:30 +01:00
2016-03-24 16:54:29 +01:00
// Masternodes will sometimes propagate votes before the transaction is known to the client,
// will check for conflicting locks and update transaction status on a new vote message
// only after the lock itself has arrived
if ( ! mapTxLockReq . count ( ctx . txHash ) & & ! mapTxLockReqRejected . count ( ctx . txHash ) ) return true ;
if ( ! FindConflictingLocks ( mapTxLockReq [ ctx . txHash ] ) ) { //?????
if ( mapTxLockReq . count ( ctx . txHash ) ) {
UpdateLockedTransaction ( mapTxLockReq [ ctx . txHash ] ) ;
LockTransactionInputs ( mapTxLockReq [ ctx . txHash ] ) ;
} else if ( mapTxLockReqRejected . count ( ctx . txHash ) ) {
ResolveConflicts ( mapTxLockReqRejected [ ctx . txHash ] ) ; ///?????
} else {
2016-05-25 07:25:16 +02:00
LogPrint ( " instantsend " , " InstantX::ProcessConsensusVote - Transaction Lock Request is missing %s ! votes %d \n " , ctx . GetHash ( ) . ToString ( ) , nSignatures ) ;
2015-02-05 17:48:57 +01:00
}
2015-02-03 23:40:00 +01:00
}
2014-12-09 02:17:57 +01:00
}
2015-02-02 15:36:00 +01:00
return true ;
2014-12-09 02:17:57 +01:00
}
2015-02-01 16:53:49 +01:00
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-03-24 16:54:29 +01:00
void UpdateLockedTransaction ( CTransaction & tx , bool fForceNotification ) {
// there should be no conflicting locks
if ( FindConflictingLocks ( tx ) ) return ;
uint256 txHash = tx . GetHash ( ) ;
// there must be a successfully verified lock request
if ( ! mapTxLockReq . count ( txHash ) ) return ;
2016-07-15 08:38:33 +02:00
int nSignatures = GetTransactionLockSignatures ( txHash ) ;
2016-03-24 16:54:29 +01:00
# ifdef ENABLE_WALLET
if ( pwalletMain & & pwalletMain - > UpdatedTransaction ( txHash ) ) {
// bumping this to update UI
nCompleteTXLocks + + ;
// a transaction lock must have enough signatures to trigger this notification
if ( nSignatures = = INSTANTX_SIGNATURES_REQUIRED | | ( fForceNotification & & nSignatures > INSTANTX_SIGNATURES_REQUIRED ) ) {
// notify an external script once threshold is reached
2016-05-25 07:25:16 +02:00
std : : string strCmd = GetArg ( " -instantsendnotify " , " " ) ;
2016-03-24 16:54:29 +01:00
if ( ! strCmd . empty ( ) )
{
boost : : replace_all ( strCmd , " %s " , txHash . GetHex ( ) ) ;
boost : : thread t ( runCommand , strCmd ) ; // thread runs free
}
}
}
# endif
2016-07-15 08:38:33 +02:00
if ( nSignatures = = INSTANTX_SIGNATURES_REQUIRED | | ( fForceNotification & & nSignatures > INSTANTX_SIGNATURES_REQUIRED ) ) {
GetMainSignals ( ) . NotifyTransactionLock ( tx ) ;
}
2016-03-24 16:54:29 +01:00
}
void LockTransactionInputs ( CTransaction & tx ) {
if ( mapTxLockReq . count ( tx . GetHash ( ) ) ) {
BOOST_FOREACH ( const CTxIn & in , tx . vin ) {
if ( ! mapLockedInputs . count ( in . prevout ) ) {
mapLockedInputs . insert ( make_pair ( in . prevout , tx . GetHash ( ) ) ) ;
}
}
}
}
bool FindConflictingLocks ( CTransaction & tx )
2015-02-05 17:48:57 +01:00
{
/*
It ' s possible ( very unlikely though ) to get 2 conflicting transaction locks approved by the network .
In that case , they will cancel each other out .
Blocks could have been rejected during this time , which is OK . After they cancel out , the client will
rescan the blocks and find they ' re acceptable and then take the chain with the most work .
*/
BOOST_FOREACH ( const CTxIn & in , tx . vin ) {
if ( mapLockedInputs . count ( in . prevout ) ) {
if ( mapLockedInputs [ in . prevout ] ! = tx . GetHash ( ) ) {
2016-03-24 16:54:29 +01:00
LogPrintf ( " InstantX::FindConflictingLocks - found two complete conflicting locks - removing both. %s %s " , tx . GetHash ( ) . ToString ( ) , mapLockedInputs [ in . prevout ] . ToString ( ) ) ;
2015-02-05 17:48:57 +01:00
if ( mapTxLocks . count ( tx . GetHash ( ) ) ) mapTxLocks [ tx . GetHash ( ) ] . nExpiration = GetTime ( ) ;
if ( mapTxLocks . count ( mapLockedInputs [ in . prevout ] ) ) mapTxLocks [ mapLockedInputs [ in . prevout ] ] . nExpiration = GetTime ( ) ;
return true ;
}
}
}
return false ;
}
2015-02-02 18:33:52 +01:00
2016-03-24 16:54:29 +01:00
void ResolveConflicts ( CTransaction & tx ) {
// resolve conflicts
if ( IsLockedIXTransaction ( tx . GetHash ( ) ) & & ! FindConflictingLocks ( tx ) ) { //?????
LogPrintf ( " ResolveConflicts - Found Existing Complete IX Lock, resolving... \n " ) ;
//reprocess the last 15 blocks
ReprocessBlocks ( 15 ) ;
if ( ! mapTxLockReq . count ( tx . GetHash ( ) ) ) mapTxLockReq . insert ( make_pair ( tx . GetHash ( ) , tx ) ) ; //?????
}
}
2015-02-02 18:33:52 +01:00
int64_t GetAverageVoteTime ( )
{
std : : map < uint256 , int64_t > : : iterator it = mapUnknownVotes . begin ( ) ;
int64_t total = 0 ;
int64_t count = 0 ;
while ( it ! = mapUnknownVotes . end ( ) ) {
total + = it - > second ;
count + + ;
it + + ;
}
return total / count ;
}
2014-12-09 02:17:57 +01:00
void CleanTransactionLocksList ( )
{
std : : map < uint256 , CTransactionLock > : : iterator it = mapTxLocks . begin ( ) ;
2014-12-31 03:54:00 +01:00
2014-12-09 02:17:57 +01:00
while ( it ! = mapTxLocks . end ( ) ) {
2015-02-01 21:04:20 +01:00
if ( GetTime ( ) > it - > second . nExpiration ) { //keep them for an hour
2016-03-04 06:58:53 +01:00
LogPrintf ( " Removing old transaction lock %s \n " , it - > second . txHash . ToString ( ) ) ;
2015-02-04 21:20:13 +01:00
if ( mapTxLockReq . count ( it - > second . txHash ) ) {
CTransaction & tx = mapTxLockReq [ it - > second . txHash ] ;
2015-02-04 21:25:12 +01:00
2015-02-04 21:20:13 +01:00
BOOST_FOREACH ( const CTxIn & in , tx . vin )
mapLockedInputs . erase ( in . prevout ) ;
mapTxLockReq . erase ( it - > second . txHash ) ;
mapTxLockReqRejected . erase ( it - > second . txHash ) ;
2015-02-06 20:58:03 +01:00
BOOST_FOREACH ( CConsensusVote & v , it - > second . vecConsensusVotes )
mapTxLockVote . erase ( v . GetHash ( ) ) ;
2015-02-04 21:20:13 +01:00
}
2014-12-09 02:17:57 +01:00
mapTxLocks . erase ( it + + ) ;
} else {
it + + ;
}
}
}
2016-03-23 15:49:35 +01:00
bool IsLockedIXTransaction ( uint256 txHash ) {
2016-03-24 16:54:29 +01:00
// there must be a successfully verified lock request...
if ( ! mapTxLockReq . count ( txHash ) ) return false ;
// ...and corresponding lock must have enough signatures
2016-03-23 15:49:35 +01:00
std : : map < uint256 , CTransactionLock > : : iterator i = mapTxLocks . find ( txHash ) ;
return i ! = mapTxLocks . end ( ) & & ( * i ) . second . CountSignatures ( ) > = INSTANTX_SIGNATURES_REQUIRED ;
}
int GetTransactionLockSignatures ( uint256 txHash )
{
if ( fLargeWorkForkFound | | fLargeWorkInvalidChainFound ) return - 2 ;
2016-07-30 13:04:27 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTX ) ) return - 3 ;
2016-05-25 07:25:16 +02:00
if ( ! fEnableInstantSend ) return - 1 ;
2016-03-23 15:49:35 +01:00
std : : map < uint256 , CTransactionLock > : : iterator i = mapTxLocks . find ( txHash ) ;
if ( i ! = mapTxLocks . end ( ) ) {
return ( * i ) . second . CountSignatures ( ) ;
}
return - 1 ;
}
bool IsTransactionLockTimedOut ( uint256 txHash )
{
2016-05-25 07:25:16 +02:00
if ( ! fEnableInstantSend ) return 0 ;
2016-03-23 15:49:35 +01:00
std : : map < uint256 , CTransactionLock > : : iterator i = mapTxLocks . find ( txHash ) ;
if ( i ! = mapTxLocks . end ( ) ) {
return GetTime ( ) > ( * i ) . second . nTimeout ;
}
return false ;
}
2015-02-01 16:53:49 +01:00
uint256 CConsensusVote : : GetHash ( ) const
{
2016-02-02 16:28:56 +01:00
return ArithToUint256 ( UintToArith256 ( vinMasternode . prevout . hash ) + vinMasternode . prevout . n + UintToArith256 ( txHash ) ) ;
2015-02-01 16:53:49 +01:00
}
2014-12-09 02:17:57 +01:00
bool CConsensusVote : : SignatureValid ( )
{
std : : string errorMessage ;
2015-02-02 15:36:00 +01:00
std : : string strMessage = txHash . ToString ( ) . c_str ( ) + boost : : lexical_cast < std : : string > ( nBlockHeight ) ;
2016-03-04 06:58:53 +01:00
//LogPrintf("verify strMessage %s \n", strMessage);
2014-12-31 03:54:00 +01:00
2015-02-25 12:54:03 +01:00
CMasternode * pmn = mnodeman . Find ( vinMasternode ) ;
2014-12-09 02:17:57 +01:00
2015-02-25 12:54:03 +01:00
if ( pmn = = NULL )
2014-12-09 02:17:57 +01:00
{
2016-03-04 06:58:53 +01:00
LogPrintf ( " InstantX::CConsensusVote::SignatureValid() - Unknown Masternode %s \n " , vinMasternode . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2015-02-25 12:54:03 +01:00
if ( ! darkSendSigner . VerifyMessage ( pmn - > pubkey2 , vchMasterNodeSignature , strMessage , errorMessage ) ) {
2014-12-09 02:17:57 +01:00
LogPrintf ( " InstantX::CConsensusVote::SignatureValid() - Verify message failed \n " ) ;
return false ;
}
return true ;
}
bool CConsensusVote : : Sign ( )
{
std : : string errorMessage ;
2015-02-02 15:36:00 +01:00
std : : string strMessage = txHash . ToString ( ) . c_str ( ) + boost : : lexical_cast < std : : string > ( nBlockHeight ) ;
2016-03-04 06:58:53 +01:00
//LogPrintf("signing strMessage %s \n", strMessage);
2014-12-09 02:17:57 +01:00
2016-07-15 08:36:00 +02:00
if ( ! darkSendSigner . SignMessage ( strMessage , errorMessage , vchMasterNodeSignature , activeMasternode . keyMasternode ) ) {
2015-06-24 23:27:56 +02:00
LogPrintf ( " CConsensusVote::Sign() - Sign message failed " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-07-15 08:36:00 +02:00
if ( ! darkSendSigner . VerifyMessage ( activeMasternode . pubKeyMasternode , vchMasterNodeSignature , strMessage , errorMessage ) ) {
2015-06-24 23:27:56 +02:00
LogPrintf ( " CConsensusVote::Sign() - Verify message failed " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
return true ;
}
bool CTransactionLock : : SignaturesValid ( )
{
BOOST_FOREACH ( CConsensusVote vote , vecConsensusVotes )
{
2015-02-23 21:01:21 +01:00
int n = mnodeman . GetMasternodeRank ( vote . vinMasternode , vote . nBlockHeight , MIN_INSTANTX_PROTO_VERSION ) ;
2014-12-09 02:17:57 +01:00
2014-12-31 03:54:00 +01:00
if ( n = = - 1 )
2014-12-09 02:17:57 +01:00
{
2016-03-04 06:58:53 +01:00
LogPrintf ( " CTransactionLock::SignaturesValid() - Unknown Masternode %s \n " , vote . vinMasternode . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2015-02-05 18:56:11 +01:00
if ( n > INSTANTX_SIGNATURES_TOTAL )
2014-12-09 02:17:57 +01:00
{
2015-06-24 23:27:56 +02:00
LogPrintf ( " CTransactionLock::SignaturesValid() - Masternode not in the top %s \n " , INSTANTX_SIGNATURES_TOTAL ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
if ( ! vote . SignatureValid ( ) ) {
2015-06-24 23:27:56 +02:00
LogPrintf ( " CTransactionLock::SignaturesValid() - Signature not valid \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
return true ;
}
2015-02-06 20:07:22 +01:00
void CTransactionLock : : AddSignature ( CConsensusVote & cv )
2014-12-09 02:17:57 +01:00
{
vecConsensusVotes . push_back ( cv ) ;
}
int CTransactionLock : : CountSignatures ( )
{
2015-02-04 22:09:50 +01:00
/*
Only count signatures where the BlockHeight matches the transaction ' s blockheight .
The votes have no proof it ' s the correct blockheight
*/
2015-02-05 05:05:36 +01:00
if ( nBlockHeight = = 0 ) return - 1 ;
2015-02-04 22:09:50 +01:00
int n = 0 ;
BOOST_FOREACH ( CConsensusVote v , vecConsensusVotes ) {
if ( v . nBlockHeight = = nBlockHeight ) {
n + + ;
}
}
return n ;
2014-12-06 20:41:53 +01:00
}