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
2016-08-05 21:49:45 +02:00
extern CWallet * pwalletMain ;
bool fEnableInstantSend = true ;
int nInstantSendDepth = DEFAULT_INSTANTSEND_DEPTH ;
int nCompleteTXLocks ;
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 ;
2015-02-04 21:20:13 +01:00
std : : map < COutPoint , uint256 > mapLockedInputs ;
2016-08-05 21:49:45 +02:00
std : : map < uint256 , CTransactionLock > mapTxLocks ;
2015-02-02 18:33:52 +01:00
std : : map < uint256 , int64_t > mapUnknownVotes ; //track votes with no tx for DOS
2016-08-05 21:49:45 +02:00
CCriticalSection cs_instantsend ;
2014-12-09 02:17:57 +01:00
//txlock - Locks transaction
//
//step 1.) Broadcast intention to lock transaction inputs, "txlreg", CTransaction
2016-08-05 21:49:45 +02:00
//step 2.) Top INSTANTSEND_SIGNATURES_TOTAL masternodes, open connect to top 1 masternode.
2015-06-04 20:54:33 +02:00
// Send "txvote", CTransaction, Signature, Approve
2016-08-05 21:49:45 +02:00
//step 3.) Top 1 masternode, waits for INSTANTSEND_SIGNATURES_REQUIRED messages. Upon success, sends "txlock'
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
void ProcessMessageInstantSend ( CNode * pfrom , std : : string & strCommand , CDataStream & vRecv )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
if ( fLiteMode ) return ; // disable all Dash specific functionality
2016-09-01 16:55:25 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2016-08-29 21:11:34 +02:00
// Ignore any InstantSend messages until masternode list is synced
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) return ;
2014-12-09 02:17:57 +01:00
2016-09-15 22:07:01 +02:00
if ( strCommand = = NetMsgType : : TXLOCKREQUEST ) // InstantSend Transaction Lock Request
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
//LogPrintf("ProcessMessageInstantSend\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?
2016-08-05 21:49:45 +02:00
if ( ! IsInstantSendTxValid ( tx ) ) return ;
2015-02-04 11:44:41 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxOut o , tx . vout ) {
2016-09-02 14:17:32 +02:00
// InstandSend supports normal scripts and unspendable scripts (used in PrivateSend collateral and Governance collateral).
2015-07-12 23:02:39 +02:00
// TODO: Look into other script types that are normal and can be included
2016-08-05 21:49:45 +02:00
if ( ! o . scriptPubKey . IsNormalPaymentScript ( ) & & ! o . scriptPubKey . IsUnspendable ( ) ) {
LogPrintf ( " ProcessMessageInstantSend -- 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 ) ;
}
2016-08-05 21:49:45 +02:00
if ( fAccepted ) {
2015-08-05 00:49:34 +02:00
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
2016-08-05 21:49:45 +02:00
mapTxLockReq . insert ( std : : make_pair ( tx . GetHash ( ) , tx ) ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
LogPrintf ( " ProcessMessageInstantSend -- 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
2016-08-05 21:49:45 +02:00
if ( IsLockedInstandSendTransaction ( tx . GetHash ( ) ) ) {
2016-03-24 16:54:29 +01:00
UpdateLockedTransaction ( tx , true ) ;
LockTransactionInputs ( tx ) ;
ResolveConflicts ( tx ) ;
}
2014-12-09 02:17:57 +01:00
return ;
} else {
2016-08-05 21:49:45 +02:00
mapTxLockReqRejected . insert ( std : : make_pair ( tx . GetHash ( ) , tx ) ) ;
2014-12-09 02:17:57 +01:00
// can we get the conflicting transaction as proof?
2016-08-05 21:49:45 +02:00
LogPrintf ( " ProcessMessageInstantSend -- 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-09-15 22:07:01 +02:00
else if ( strCommand = = NetMsgType : : TXLOCKVOTE ) // InstantSend Transaction Lock Consensus Votes
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
CConsensusVote vote ;
vRecv > > vote ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CInv inv ( MSG_TXLOCK_VOTE , vote . GetHash ( ) ) ;
2014-12-09 02:17:57 +01:00
pfrom - > AddInventoryKnown ( inv ) ;
2016-08-05 21:49:45 +02:00
if ( mapTxLockVote . count ( vote . GetHash ( ) ) ) return ;
mapTxLockVote . insert ( std : : make_pair ( vote . GetHash ( ) , vote ) ) ;
2014-12-31 03:54:00 +01:00
2016-08-05 21:49:45 +02:00
if ( ProcessConsensusVote ( pfrom , vote ) ) {
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
*/
2016-08-05 21:49:45 +02:00
if ( ! mapTxLockReq . count ( vote . txHash ) & & ! mapTxLockReqRejected . count ( vote . txHash ) ) {
if ( ! mapUnknownVotes . count ( vote . vinMasternode . prevout . hash ) )
mapUnknownVotes [ vote . vinMasternode . prevout . hash ] = GetTime ( ) + ( 60 * 10 ) ;
if ( mapUnknownVotes [ vote . vinMasternode . prevout . hash ] > GetTime ( ) & &
mapUnknownVotes [ vote . vinMasternode . prevout . hash ] - GetAverageVoteTime ( ) > 60 * 10 ) {
LogPrintf ( " ProcessMessageInstantSend -- masternode is spamming transaction votes: %s %s \n " ,
vote . vinMasternode . ToString ( ) ,
vote . txHash . ToString ( )
2015-02-02 18:33:52 +01:00
) ;
return ;
2015-02-02 15:36:00 +01:00
} else {
2016-08-05 21:49:45 +02:00
mapUnknownVotes [ vote . vinMasternode . prevout . hash ] = GetTime ( ) + ( 60 * 10 ) ;
2015-02-02 15:36:00 +01:00
}
}
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
}
}
2016-09-05 18:09:25 +02:00
bool IsInstantSendTxValid ( const CTransaction & txCandidate )
2016-08-05 21:49:45 +02:00
{
2016-09-05 18:09:25 +02:00
if ( txCandidate . vout . size ( ) < 1 ) return false ;
2016-03-21 21:23:45 +01:00
2016-09-05 18:09:25 +02:00
if ( ! CheckFinalTx ( txCandidate ) ) {
LogPrint ( " instantsend " , " IsInstantSendTxValid -- Transaction is not final: txCandidate=%s " , txCandidate . 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 ;
2016-09-05 18:09:25 +02:00
BOOST_FOREACH ( const CTxOut txout , txCandidate . vout ) {
2016-08-05 21:49:45 +02:00
nValueOut + = txout . nValue ;
2016-09-05 18:09:25 +02:00
}
2015-02-04 11:44:41 +01:00
2016-09-05 18:09:25 +02:00
BOOST_FOREACH ( const CTxIn txin , txCandidate . vin ) {
2015-02-04 11:44:41 +01:00
CTransaction tx2 ;
uint256 hash ;
2016-08-05 21:49:45 +02:00
if ( GetTransaction ( txin . prevout . hash , tx2 , Params ( ) . GetConsensus ( ) , hash , true ) ) {
if ( tx2 . vout . size ( ) > txin . prevout . n )
nValueIn + = tx2 . vout [ txin . prevout . n ] . nValue ;
} else {
2015-02-04 11:44:41 +01:00
missingTx = true ;
}
}
2016-09-01 16:55:25 +02:00
if ( nValueOut > sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) * COIN ) {
2016-09-05 18:09:25 +02:00
LogPrint ( " instantsend " , " IsInstantSendTxValid -- Transaction value too high: nValueOut=%d, txCandidate=%s " , nValueOut , txCandidate . ToString ( ) ) ;
2015-02-11 15:47:21 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( missingTx ) {
2016-09-05 18:09:25 +02:00
LogPrint ( " instantsend " , " IsInstantSendTxValid -- Unknown inputs in IX transaction: txCandidate=%s " , txCandidate . 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-09-05 18:09:25 +02:00
if ( nValueIn - nValueOut < INSTANTSEND_MIN_FEE ) {
LogPrint ( " instantsend " , " IsInstantSendTxValid -- did not include enough fees in transaction: fees=%d, txCandidate=%s " , nValueOut - nValueIn , txCandidate . 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 ;
2016-08-05 21:49:45 +02:00
BOOST_REVERSE_FOREACH ( CTxIn txin , tx . vin ) {
nTxAge = GetInputAge ( txin ) ;
if ( nTxAge < 5 ) { //1 less than the "send IX" gui requires, incase of a block propagating the network at the time
LogPrintf ( " CreateNewLock -- Transaction not found / too new: nTxAge=%d, txid=%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 ( ) ;
2016-08-05 21:49:45 +02:00
if ( tip )
nBlockHeight = tip - > nHeight - nTxAge + 4 ;
else
return 0 ;
2016-03-02 21:57:24 +01:00
}
2015-02-04 22:09:50 +01:00
2016-08-05 21:49:45 +02:00
if ( ! mapTxLocks . count ( tx . GetHash ( ) ) ) {
LogPrintf ( " CreateNewLock -- New Transaction Lock! txid=%s \n " , tx . GetHash ( ) . ToString ( ) ) ;
2015-02-04 22:09:50 +01:00
CTransactionLock newLock ;
newLock . nBlockHeight = nBlockHeight ;
2016-06-29 20:20:18 +02:00
//locks expire after nInstantSendKeepLock confirmations
newLock . nLockExpirationBlock = chainActive . Height ( ) + Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ;
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 ( ) ;
2016-08-05 21:49:45 +02:00
mapTxLocks . insert ( std : : make_pair ( tx . GetHash ( ) , newLock ) ) ;
2015-02-04 22:09:50 +01:00
} else {
mapTxLocks [ tx . GetHash ( ) ] . nBlockHeight = nBlockHeight ;
2016-08-05 21:49:45 +02:00
LogPrint ( " instantsend " , " CreateNewLock -- Transaction Lock Exists! txid=%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 ;
2016-08-05 21:49:45 +02:00
int n = mnodeman . GetMasternodeRank ( activeMasternode . vin , nBlockHeight , MIN_INSTANTSEND_PROTO_VERSION ) ;
2015-02-06 05:41:17 +01:00
2016-08-05 21:49:45 +02:00
if ( n = = - 1 ) {
LogPrint ( " instantsend " , " DoConsensusVote -- Unknown Masternode %s \n " , activeMasternode . vin . ToString ( ) ) ;
2015-02-06 05:41:17 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( n > INSTANTSEND_SIGNATURES_TOTAL ) {
LogPrint ( " instantsend " , " DoConsensusVote -- Masternode not in the top %d (%d) \n " , INSTANTSEND_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-08-05 21:49:45 +02:00
LogPrint ( " instantsend " , " DoConsensusVote -- In the top %d (%d) \n " , INSTANTSEND_SIGNATURES_TOTAL , n ) ;
2015-02-06 05:41:17 +01:00
2016-08-05 21:49:45 +02:00
CConsensusVote vote ;
vote . vinMasternode = activeMasternode . vin ;
vote . txHash = tx . GetHash ( ) ;
vote . nBlockHeight = nBlockHeight ;
if ( ! vote . Sign ( ) ) {
LogPrintf ( " DoConsensusVote -- Failed to sign consensus vote \n " ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2016-08-12 07:55:41 +02:00
if ( ! vote . CheckSignature ( ) ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " DoConsensusVote -- Signature invalid \n " ) ;
2014-12-09 02:17:57 +01:00
return ;
}
2015-02-02 13:01:06 +01:00
2016-08-05 21:49:45 +02:00
{
LOCK ( cs_instantsend ) ;
mapTxLockVote [ vote . GetHash ( ) ] = vote ;
}
2015-02-06 20:07:22 +01:00
2016-08-05 21:49:45 +02:00
CInv inv ( MSG_TXLOCK_VOTE , vote . GetHash ( ) ) ;
2015-07-08 02:37:23 +02:00
RelayInv ( inv ) ;
2014-12-09 02:17:57 +01:00
}
//received a consensus vote
2016-08-05 21:49:45 +02:00
bool ProcessConsensusVote ( CNode * pnode , CConsensusVote & vote )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
int n = mnodeman . GetMasternodeRank ( vote . vinMasternode , vote . nBlockHeight , MIN_INSTANTSEND_PROTO_VERSION ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
CMasternode * pmn = mnodeman . Find ( vote . vinMasternode ) ;
2015-02-25 12:54:03 +01:00
if ( pmn ! = NULL )
2016-08-05 21:49:45 +02:00
LogPrint ( " instantsend " , " ProcessConsensusVote -- Masternode addr=%s, rank: %d \n " , pmn - > addr . ToString ( ) , n ) ;
2015-02-06 16:54:39 +01:00
2016-08-05 21:49:45 +02:00
if ( n = = - 1 ) {
2015-02-06 17:03:50 +01:00
//can be caused by past versions trying to vote with an invalid protocol
2016-08-05 21:49:45 +02:00
LogPrint ( " instantsend " , " ProcessConsensusVote -- Unknown Masternode: txin=%s \n " , vote . vinMasternode . ToString ( ) ) ;
mnodeman . AskForMN ( pnode , vote . vinMasternode ) ;
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( n > INSTANTSEND_SIGNATURES_TOTAL ) {
LogPrint ( " instantsend " , " ProcessConsensusVote -- Masternode not in the top %d (%d): vote hash %s \n " , INSTANTSEND_SIGNATURES_TOTAL , n , vote . GetHash ( ) . ToString ( ) ) ;
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-12 07:55:41 +02:00
if ( ! vote . CheckSignature ( ) ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " ProcessConsensusVote -- Signature invalid \n " ) ;
2015-08-07 05:07:40 +02:00
// don't ban, it could just be a non-synced masternode
2016-08-05 21:49:45 +02:00
mnodeman . AskForMN ( pnode , vote . vinMasternode ) ;
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
if ( ! mapTxLocks . count ( vote . txHash ) ) {
LogPrintf ( " ProcessConsensusVote -- New Transaction Lock! txid=%s \n " , vote . 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 ;
2016-06-29 20:20:18 +02:00
//locks expire after nInstantSendKeepLock confirmations
newLock . nLockExpirationBlock = chainActive . Height ( ) + Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ;
2015-02-05 16:52:02 +01:00
newLock . nTimeout = GetTime ( ) + ( 60 * 5 ) ;
2016-08-05 21:49:45 +02:00
newLock . txHash = vote . txHash ;
mapTxLocks . insert ( std : : make_pair ( vote . txHash , newLock ) ) ;
} else {
LogPrint ( " instantsend " , " ProcessConsensusVote -- Transaction Lock Exists! txid=%s \n " , vote . txHash . ToString ( ) ) ;
}
2015-02-01 16:53:49 +01:00
2014-12-09 02:17:57 +01:00
//compile consessus vote
2016-08-05 21:49:45 +02:00
std : : map < uint256 , CTransactionLock > : : iterator i = mapTxLocks . find ( vote . txHash ) ;
if ( i ! = mapTxLocks . end ( ) ) {
( * i ) . second . AddVote ( vote ) ;
2015-02-03 23:40:00 +01:00
2016-08-05 21:49:45 +02:00
int nSignatures = ( * i ) . second . CountVotes ( ) ;
LogPrint ( " instantsend " , " ProcessConsensusVote -- Transaction Lock signatures count: %d, vote hash=%s \n " , nSignatures , vote . GetHash ( ) . ToString ( ) ) ;
2015-02-03 23:40:00 +01:00
2016-08-05 21:49:45 +02:00
if ( nSignatures > = INSTANTSEND_SIGNATURES_REQUIRED ) {
LogPrint ( " instantsend " , " ProcessConsensusVote -- Transaction Lock Is Complete! txid=%s \n " , vote . 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
2016-08-05 21:49:45 +02:00
if ( ! mapTxLockReq . count ( vote . txHash ) & & ! mapTxLockReqRejected . count ( vote . txHash ) ) return true ;
if ( ! FindConflictingLocks ( mapTxLockReq [ vote . txHash ] ) ) { //?????
if ( mapTxLockReq . count ( vote . txHash ) ) {
UpdateLockedTransaction ( mapTxLockReq [ vote . txHash ] ) ;
LockTransactionInputs ( mapTxLockReq [ vote . txHash ] ) ;
} else if ( mapTxLockReqRejected . count ( vote . txHash ) ) {
ResolveConflicts ( mapTxLockReqRejected [ vote . txHash ] ) ; ///?????
2016-03-24 16:54:29 +01:00
} else {
2016-08-05 21:49:45 +02:00
LogPrint ( " instantsend " , " ProcessConsensusVote -- Transaction Lock Request is missing! nSignatures=%d, vote hash %s \n " , nSignatures , vote . GetHash ( ) . ToString ( ) ) ;
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-08-05 21:49:45 +02:00
void UpdateLockedTransaction ( CTransaction & tx , bool fForceNotification )
{
2016-03-24 16:54:29 +01:00
// there should be no conflicting locks
if ( FindConflictingLocks ( tx ) ) return ;
uint256 txHash = tx . GetHash ( ) ;
// there must be a successfully verified lock request
2016-08-05 21:49:45 +02:00
if ( ! mapTxLockReq . count ( txHash ) ) return ;
2016-03-24 16:54:29 +01:00
2016-07-15 08:38:33 +02:00
int nSignatures = GetTransactionLockSignatures ( txHash ) ;
2016-03-24 16:54:29 +01:00
# ifdef ENABLE_WALLET
2016-08-05 21:49:45 +02:00
if ( pwalletMain & & pwalletMain - > UpdatedTransaction ( txHash ) ) {
2016-03-24 16:54:29 +01:00
// bumping this to update UI
nCompleteTXLocks + + ;
// a transaction lock must have enough signatures to trigger this notification
2016-08-05 21:49:45 +02:00
if ( nSignatures = = INSTANTSEND_SIGNATURES_REQUIRED | | ( fForceNotification & & nSignatures > INSTANTSEND_SIGNATURES_REQUIRED ) ) {
2016-03-24 16:54:29 +01:00
// notify an external script once threshold is reached
2016-05-25 07:25:16 +02:00
std : : string strCmd = GetArg ( " -instantsendnotify " , " " ) ;
2016-08-05 21:49:45 +02:00
if ( ! strCmd . empty ( ) ) {
2016-03-24 16:54:29 +01:00
boost : : replace_all ( strCmd , " %s " , txHash . GetHex ( ) ) ;
boost : : thread t ( runCommand , strCmd ) ; // thread runs free
}
}
}
# endif
2016-07-15 08:38:33 +02:00
2016-08-05 21:49:45 +02:00
if ( nSignatures = = INSTANTSEND_SIGNATURES_REQUIRED | | ( fForceNotification & & nSignatures > INSTANTSEND_SIGNATURES_REQUIRED ) )
2016-07-15 08:38:33 +02:00
GetMainSignals ( ) . NotifyTransactionLock ( tx ) ;
2016-03-24 16:54:29 +01:00
}
void LockTransactionInputs ( CTransaction & tx ) {
2016-08-05 21:49:45 +02:00
if ( ! mapTxLockReq . count ( tx . GetHash ( ) ) ) return ;
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
if ( ! mapLockedInputs . count ( txin . prevout ) )
mapLockedInputs . insert ( std : : make_pair ( txin . prevout , tx . GetHash ( ) ) ) ;
2016-03-24 16:54:29 +01:00
}
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 .
*/
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxIn & txin , tx . vin ) {
if ( mapLockedInputs . count ( txin . prevout ) ) {
if ( mapLockedInputs [ txin . prevout ] ! = tx . GetHash ( ) ) {
LogPrintf ( " FindConflictingLocks -- found two complete conflicting locks, removing both: txid=%s, txin=%s " , tx . GetHash ( ) . ToString ( ) , mapLockedInputs [ txin . prevout ] . ToString ( ) ) ;
if ( mapTxLocks . count ( tx . GetHash ( ) ) )
2016-06-29 20:20:18 +02:00
mapTxLocks [ tx . GetHash ( ) ] . nLockExpirationBlock = - 1 ;
2016-08-05 21:49:45 +02:00
if ( mapTxLocks . count ( mapLockedInputs [ txin . prevout ] ) )
2016-06-29 20:20:18 +02:00
mapTxLocks [ mapLockedInputs [ txin . prevout ] ] . nLockExpirationBlock = - 1 ;
2016-08-05 21:49:45 +02:00
2015-02-05 17:48:57 +01:00
return true ;
}
}
}
return false ;
}
2015-02-02 18:33:52 +01:00
2016-08-05 21:49:45 +02:00
void ResolveConflicts ( CTransaction & tx )
{
2016-03-24 16:54:29 +01:00
// resolve conflicts
2016-08-05 21:49:45 +02:00
if ( IsLockedInstandSendTransaction ( tx . GetHash ( ) ) & & ! FindConflictingLocks ( tx ) ) { //?????
LogPrintf ( " ResolveConflicts -- Found Existing Complete IX Lock, resolving... \n " ) ;
2016-03-24 16:54:29 +01:00
2016-06-29 20:20:18 +02:00
//reprocess the last nInstantSendReprocessBlocks blocks
ReprocessBlocks ( Params ( ) . GetConsensus ( ) . nInstantSendReprocessBlocks ) ;
2016-08-05 21:49:45 +02:00
if ( ! mapTxLockReq . count ( tx . GetHash ( ) ) )
mapTxLockReq . insert ( std : : make_pair ( tx . GetHash ( ) , tx ) ) ; //?????
2016-03-24 16:54:29 +01:00
}
}
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 ( )
{
2016-08-05 21:49:45 +02:00
LOCK ( cs_instantsend ) ;
2014-12-09 02:17:57 +01:00
std : : map < uint256 , CTransactionLock > : : iterator it = mapTxLocks . begin ( ) ;
2014-12-31 03:54:00 +01:00
2016-06-29 20:20:18 +02:00
int nHeight = chainActive . Height ( ) ;
2014-12-09 02:17:57 +01:00
while ( it ! = mapTxLocks . end ( ) ) {
2016-08-05 21:49:45 +02:00
CTransactionLock & txLock = it - > second ;
2016-08-12 07:45:06 +02:00
if ( nHeight > txLock . nLockExpirationBlock ) {
2016-08-05 21:49:45 +02:00
LogPrintf ( " Removing old transaction lock: txid=%s \n " , txLock . txHash . ToString ( ) ) ;
2015-02-04 21:20:13 +01:00
2016-08-05 21:49:45 +02:00
if ( mapTxLockReq . count ( txLock . txHash ) ) {
CTransaction & tx = mapTxLockReq [ txLock . txHash ] ;
2015-02-04 21:25:12 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
mapLockedInputs . erase ( txin . prevout ) ;
2015-02-04 21:20:13 +01:00
2016-08-05 21:49:45 +02:00
mapTxLockReq . erase ( txLock . txHash ) ;
mapTxLockReqRejected . erase ( txLock . txHash ) ;
2015-02-06 20:58:03 +01:00
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CConsensusVote & vote , txLock . vecConsensusVotes )
if ( mapTxLockVote . count ( vote . GetHash ( ) ) )
mapTxLockVote . erase ( vote . GetHash ( ) ) ;
2015-02-04 21:20:13 +01:00
}
2014-12-09 02:17:57 +01:00
mapTxLocks . erase ( it + + ) ;
} else {
it + + ;
}
}
}
2016-08-05 21:49:45 +02:00
bool IsLockedInstandSendTransaction ( 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 ) ;
2016-08-05 21:49:45 +02:00
return i ! = mapTxLocks . end ( ) & & ( * i ) . second . CountVotes ( ) > = INSTANTSEND_SIGNATURES_REQUIRED ;
2016-03-23 15:49:35 +01:00
}
int GetTransactionLockSignatures ( uint256 txHash )
{
2016-09-05 18:09:25 +02:00
if ( ! fEnableInstantSend ) return - 1 ;
2016-03-23 15:49:35 +01:00
if ( fLargeWorkForkFound | | fLargeWorkInvalidChainFound ) return - 2 ;
2016-09-01 16:55:25 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return - 3 ;
2016-03-23 15:49:35 +01:00
2016-08-05 21:49:45 +02:00
std : : map < uint256 , CTransactionLock > : : iterator it = mapTxLocks . find ( txHash ) ;
if ( it ! = mapTxLocks . end ( ) ) return it - > second . CountVotes ( ) ;
2016-03-23 15:49:35 +01:00
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 ) ;
2016-08-05 21:49:45 +02:00
if ( i ! = mapTxLocks . end ( ) ) return GetTime ( ) > ( * i ) . second . nTimeout ;
2016-03-23 15:49:35 +01:00
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
}
2016-08-12 07:55:41 +02:00
bool CConsensusVote : : CheckSignature ( )
2014-12-09 02:17:57 +01:00
{
2016-08-12 07:55:41 +02:00
std : : string strError ;
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
2016-08-05 21:49:45 +02:00
if ( pmn = = NULL ) {
2016-08-12 07:55:41 +02:00
LogPrintf ( " CConsensusVote::CheckSignature -- Unknown Masternode: txin=%s \n " , vinMasternode . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-09-16 00:00:06 +02:00
if ( ! darkSendSigner . VerifyMessage ( pmn - > pubKeyMasternode , vchMasterNodeSignature , strMessage , strError ) ) {
2016-08-19 13:50:04 +02:00
LogPrintf ( " CConsensusVote::CheckSignature -- VerifyMessage() failed, error: %s \n " , strError ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
return true ;
}
bool CConsensusVote : : Sign ( )
{
2016-08-12 07:55:41 +02:00
std : : string strError ;
2014-12-09 02:17:57 +01:00
2015-02-02 15:36:00 +01:00
std : : string strMessage = txHash . ToString ( ) . c_str ( ) + boost : : lexical_cast < std : : string > ( nBlockHeight ) ;
2014-12-09 02:17:57 +01:00
2016-08-19 13:50:04 +02:00
if ( ! darkSendSigner . SignMessage ( strMessage , vchMasterNodeSignature , activeMasternode . keyMasternode ) ) {
LogPrintf ( " CConsensusVote::Sign -- SignMessage() failed \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-12 07:55:41 +02:00
if ( ! darkSendSigner . VerifyMessage ( activeMasternode . pubKeyMasternode , vchMasterNodeSignature , strMessage , strError ) ) {
2016-08-19 13:50:04 +02:00
LogPrintf ( " CConsensusVote::Sign -- VerifyMessage() failed, error: %s \n " , strError ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
return true ;
}
2016-08-12 07:55:41 +02:00
bool CTransactionLock : : IsAllVotesValid ( )
2014-12-09 02:17:57 +01:00
{
BOOST_FOREACH ( CConsensusVote vote , vecConsensusVotes )
{
2016-08-05 21:49:45 +02:00
int n = mnodeman . GetMasternodeRank ( vote . vinMasternode , vote . nBlockHeight , MIN_INSTANTSEND_PROTO_VERSION ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( n = = - 1 ) {
2016-08-12 07:55:41 +02:00
LogPrintf ( " CTransactionLock::IsAllVotesValid -- Unknown Masternode, txin=%s \n " , vote . vinMasternode . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-05 21:49:45 +02:00
if ( n > INSTANTSEND_SIGNATURES_TOTAL ) {
2016-08-12 07:55:41 +02:00
LogPrintf ( " CTransactionLock::IsAllVotesValid -- Masternode not in the top %s \n " , INSTANTSEND_SIGNATURES_TOTAL ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-08-12 07:55:41 +02:00
if ( ! vote . CheckSignature ( ) ) {
LogPrintf ( " CTransactionLock::IsAllVotesValid -- Signature not valid \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
return true ;
}
2016-08-05 21:49:45 +02:00
void CTransactionLock : : AddVote ( CConsensusVote & vote )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
vecConsensusVotes . push_back ( vote ) ;
2014-12-09 02:17:57 +01:00
}
2016-08-05 21:49:45 +02:00
int CTransactionLock : : CountVotes ( )
2014-12-09 02:17:57 +01:00
{
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
2016-08-05 21:49:45 +02:00
int nCount = 0 ;
BOOST_FOREACH ( CConsensusVote vote , vecConsensusVotes )
if ( vote . nBlockHeight = = nBlockHeight )
nCount + + ;
return nCount ;
2014-12-06 20:41:53 +01:00
}