2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 The Dash Core developers
2016-02-02 16:28: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-12-20 14:27:59 +01:00
2014-12-09 02:17:57 +01:00
# include "activemasternode.h"
# include "darksend.h"
2016-12-20 14:27:59 +01:00
# include "instantx.h"
# include "key.h"
2016-01-24 20:05:31 +01:00
# include "masternode-sync.h"
# include "masternodeman.h"
2016-12-20 14:27:59 +01:00
# include "net.h"
# include "protocol.h"
2015-02-09 20:28:29 +01:00
# include "spork.h"
2016-12-20 14:27:59 +01:00
# include "sync.h"
# include "util.h"
# include "consensus/validation.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
2016-11-17 01:31:35 +01:00
std : : map < uint256 , CTransaction > mapLockRequestAccepted ;
std : : map < uint256 , CTransaction > mapLockRequestRejected ;
std : : map < uint256 , CTxLockVote > mapTxLockVotes ;
2016-11-22 15:54:26 +01:00
std : : map < uint256 , CTxLockVote > mapTxLockVotesOrphan ;
2015-02-04 21:20:13 +01:00
std : : map < COutPoint , uint256 > mapLockedInputs ;
2016-08-05 21:49:45 +02:00
2016-11-17 01:31:35 +01:00
std : : map < uint256 , CTxLockCandidate > mapTxLockCandidates ;
2016-11-22 15:54:26 +01:00
std : : map < COutPoint , int64_t > mapMasternodeOrphanVotes ; //track masternodes who voted with no txreq (for DOS protection)
2016-08-05 21:49:45 +02:00
CCriticalSection cs_instantsend ;
2014-12-09 02:17:57 +01:00
2016-11-21 21:40:32 +01:00
// Transaction Locks
2014-12-09 02:17:57 +01:00
//
2016-11-21 21:40:32 +01:00
// step 1) Some node announces intention to lock transaction inputs via "txlreg" message
// step 2) Top INSTANTSEND_SIGNATURES_TOTAL masternodes push "txvote" message
// step 3) Once there are INSTANTSEND_SIGNATURES_REQUIRED valid "txvote" messages
// for a corresponding "txlreg" message, all inputs from that tx are treated as locked
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-11-22 15:54:26 +01:00
// NOTE: NetMsgType::TXLOCKREQUEST is handled via ProcessMessage() in main.cpp
2014-12-09 02:17:57 +01:00
2016-11-22 15:54:26 +01:00
if ( strCommand = = NetMsgType : : TXLOCKVOTE ) // InstantSend Transaction Lock Consensus Votes
2014-12-09 02:17:57 +01:00
{
2016-11-17 01:31:35 +01:00
CTxLockVote vote ;
2016-08-05 21:49:45 +02:00
vRecv > > vote ;
2014-12-09 02:17:57 +01:00
2017-01-18 07:38:13 +01:00
LOCK2 ( cs_main , cs_instantsend ) ;
2016-11-17 01:31:35 +01:00
if ( mapTxLockVotes . count ( vote . GetHash ( ) ) ) return ;
mapTxLockVotes . insert ( std : : make_pair ( vote . GetHash ( ) , vote ) ) ;
2014-12-31 03:54:00 +01:00
2016-11-22 15:54:26 +01:00
ProcessTxLockVote ( pfrom , vote ) ;
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-11-28 15:21:50 +01:00
{
LOCK ( cs_main ) ;
if ( ! CheckFinalTx ( txCandidate ) ) {
LogPrint ( " instantsend " , " IsInstantSendTxValid -- Transaction is not final: txCandidate=%s " , txCandidate . ToString ( ) ) ;
return false ;
}
2016-03-21 21:23:45 +01:00
}
2015-02-04 11:44:41 +01:00
int64_t nValueIn = 0 ;
int64_t nValueOut = 0 ;
2016-10-22 18:52:14 +02:00
bool fMissingInputs = false ;
2015-02-04 11:44:41 +01:00
2016-10-22 18:52:14 +02:00
BOOST_FOREACH ( const CTxOut & txout , txCandidate . vout ) {
2016-12-08 21:03:57 +01:00
// InstandSend supports normal scripts and unspendable (i.e. data) scripts.
// TODO: Look into other script types that are normal and can be included
if ( ! txout . scriptPubKey . IsNormalPaymentScript ( ) & & ! txout . scriptPubKey . IsUnspendable ( ) ) {
LogPrint ( " instantsend " , " IsInstantSendTxValid -- Invalid Script %s " , txCandidate . ToString ( ) ) ;
return false ;
}
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-10-22 18:52:14 +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 {
2016-10-22 18:52:14 +02:00
fMissingInputs = true ;
2015-02-04 11:44:41 +01:00
}
}
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-10-22 18:52:14 +02:00
if ( fMissingInputs ) {
LogPrint ( " instantsend " , " IsInstantSendTxValid -- Unknown inputs in 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 ;
}
2016-11-22 15:54:26 +01:00
bool ProcessTxLockRequest ( CNode * pfrom , const CTransaction & tx )
{
if ( ! IsInstantSendTxValid ( tx ) ) return false ;
int nBlockHeight = CreateTxLockCandidate ( tx ) ;
if ( ! nBlockHeight ) {
// smth is not right
return false ;
}
2017-01-18 07:38:13 +01:00
LOCK2 ( cs_main , cs_instantsend ) ;
2016-11-22 15:54:26 +01:00
uint256 txHash = tx . GetHash ( ) ;
mapLockRequestAccepted . insert ( std : : make_pair ( txHash , tx ) ) ;
LogPrintf ( " TXLOCKREQUEST -- Transaction Lock Request: %s %s : accepted %s \n " ,
pfrom ? pfrom - > addr . ToString ( ) : " " , pfrom ? pfrom - > cleanSubVer : " " , txHash . ToString ( ) ) ;
CreateTxLockVote ( tx , nBlockHeight ) ;
ProcessOrphanTxLockVotes ( ) ;
// 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 ( IsLockedInstandSendTransaction ( txHash ) ) {
UpdateLockedTransaction ( tx , true ) ;
LockTransactionInputs ( tx ) ;
ResolveConflicts ( tx ) ;
}
return true ;
}
2016-11-21 21:40:32 +01:00
int64_t CreateTxLockCandidate ( const CTransaction & tx )
2014-12-09 02:17:57 +01:00
{
2016-11-21 21:40:32 +01:00
// Find the age of the first input but all inputs must be old enough too
2015-02-04 22:59:19 +01:00
int64_t nTxAge = 0 ;
2016-11-21 21:40:32 +01:00
BOOST_REVERSE_FOREACH ( const CTxIn & txin , tx . vin ) {
2016-08-05 21:49:45 +02:00
nTxAge = GetInputAge ( txin ) ;
if ( nTxAge < 5 ) { //1 less than the "send IX" gui requires, incase of a block propagating the network at the time
2016-11-17 01:31:35 +01:00
LogPrintf ( " CreateTxLockCandidate -- 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-11-21 21:40:32 +01:00
int nCurrentHeight = 0 ;
int nLockInputHeight = 0 ;
2016-03-02 21:57:24 +01:00
{
LOCK ( cs_main ) ;
2016-11-21 21:40:32 +01:00
if ( ! chainActive . Tip ( ) ) return 0 ;
nCurrentHeight = chainActive . Height ( ) ;
nLockInputHeight = nCurrentHeight - nTxAge + 4 ;
2016-03-02 21:57:24 +01:00
}
2015-02-04 22:09:50 +01:00
2016-11-21 21:40:32 +01:00
uint256 txHash = tx . GetHash ( ) ;
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-21 21:40:32 +01:00
if ( ! mapTxLockCandidates . count ( txHash ) ) {
LogPrintf ( " CreateTxLockCandidate -- New Transaction Lock Candidate! txid=%s \n " , txHash . ToString ( ) ) ;
2015-02-04 22:09:50 +01:00
2016-11-17 01:31:35 +01:00
CTxLockCandidate txLockCandidate ;
2016-11-21 21:40:32 +01:00
txLockCandidate . nBlockHeight = nLockInputHeight ;
2016-06-29 20:20:18 +02:00
//locks expire after nInstantSendKeepLock confirmations
2016-11-21 21:40:32 +01:00
txLockCandidate . nExpirationBlock = nCurrentHeight + Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ;
2016-11-17 01:31:35 +01:00
txLockCandidate . nTimeout = GetTime ( ) + ( 60 * 5 ) ;
2016-11-21 21:40:32 +01:00
txLockCandidate . txHash = txHash ;
mapTxLockCandidates . insert ( std : : make_pair ( txHash , txLockCandidate ) ) ;
2015-02-04 22:09:50 +01:00
} else {
2016-11-21 21:40:32 +01:00
mapTxLockCandidates [ txHash ] . nBlockHeight = nLockInputHeight ;
LogPrint ( " instantsend " , " CreateTxLockCandidate -- Transaction Lock Candidate exists! txid=%s \n " , txHash . ToString ( ) ) ;
2015-02-04 22:09:50 +01:00
}
2016-11-21 21:40:32 +01:00
return nLockInputHeight ;
2015-02-04 22:59:19 +01:00
}
// check if we need to vote on this transaction
2016-11-21 21:40:32 +01:00
void CreateTxLockVote ( const CTransaction & tx , int nBlockHeight )
2015-02-04 22:59:19 +01:00
{
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 ) {
2016-11-17 01:31:35 +01:00
LogPrint ( " instantsend " , " CreateTxLockVote -- Unknown Masternode %s \n " , activeMasternode . vin . prevout . ToStringShort ( ) ) ;
2015-02-06 05:41:17 +01:00
return ;
}
2016-08-05 21:49:45 +02:00
if ( n > INSTANTSEND_SIGNATURES_TOTAL ) {
2016-11-17 01:31:35 +01:00
LogPrint ( " instantsend " , " CreateTxLockVote -- 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-11-17 01:31:35 +01:00
LogPrint ( " instantsend " , " CreateTxLockVote -- In the top %d (%d) \n " , INSTANTSEND_SIGNATURES_TOTAL , n ) ;
2015-02-06 05:41:17 +01:00
2016-11-17 01:31:35 +01:00
CTxLockVote vote ;
2016-08-05 21:49:45 +02:00
vote . vinMasternode = activeMasternode . vin ;
vote . txHash = tx . GetHash ( ) ;
vote . nBlockHeight = nBlockHeight ;
if ( ! vote . Sign ( ) ) {
2016-11-17 01:31:35 +01:00
LogPrintf ( " CreateTxLockVote -- 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-11-17 01:31:35 +01:00
LogPrintf ( " CreateTxLockVote -- 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 ) ;
2016-11-17 01:31:35 +01:00
mapTxLockVotes [ vote . GetHash ( ) ] = vote ;
2016-08-05 21:49:45 +02:00
}
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-11-17 01:31:35 +01:00
bool ProcessTxLockVote ( CNode * pnode , CTxLockVote & vote )
2014-12-09 02:17:57 +01:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-22 15:54:26 +01:00
// Masternodes will sometimes propagate votes before the transaction is known to the client,
// will actually process only after the lock request itself has arrived
if ( ! mapLockRequestAccepted . count ( vote . txHash ) ) {
if ( ! mapTxLockVotesOrphan . count ( vote . GetHash ( ) ) ) {
LogPrint ( " instantsend " , " ProcessTxLockVote -- Orphan vote: txid=%s masternode=%s new \n " , vote . txHash . ToString ( ) , vote . vinMasternode . prevout . ToStringShort ( ) ) ;
vote . nOrphanExpireTime = GetTime ( ) + 60 ; // keep orphan votes for 1 minute
mapTxLockVotesOrphan [ vote . GetHash ( ) ] = vote ;
} else {
LogPrint ( " instantsend " , " ProcessTxLockVote -- Orphan vote: txid=%s masternode=%s seen \n " , vote . txHash . ToString ( ) , vote . vinMasternode . prevout . ToStringShort ( ) ) ;
}
// This tracks those messages and allows only the same rate as of the rest of the network
int nMasternodeOrphanExpireTime = GetTime ( ) + 60 * 10 ; // keep time data for 10 minutes
if ( ! mapMasternodeOrphanVotes . count ( vote . vinMasternode . prevout ) ) {
mapMasternodeOrphanVotes [ vote . vinMasternode . prevout ] = nMasternodeOrphanExpireTime ;
} else {
int64_t nPrevOrphanVote = mapMasternodeOrphanVotes [ vote . vinMasternode . prevout ] ;
if ( nPrevOrphanVote > GetTime ( ) & & nPrevOrphanVote > GetAverageMasternodeOrphanVoteTime ( ) ) {
LogPrint ( " instantsend " , " ProcessTxLockVote -- masternode is spamming orphan Transaction Lock Votes: txid=%s masternode=%s \n " ,
vote . vinMasternode . prevout . ToStringShort ( ) , vote . txHash . ToString ( ) ) ;
// Misbehaving(pfrom->id, 1);
return false ;
}
// not spamming, refresh
mapMasternodeOrphanVotes [ vote . vinMasternode . prevout ] = nMasternodeOrphanExpireTime ;
}
return true ;
}
2014-12-09 02:17:57 +01:00
2016-11-21 21:40:32 +01:00
LogPrint ( " instantsend " , " ProcessTxLockVote -- Transaction Lock Vote, txid=%s \n " , vote . txHash . ToString ( ) ) ;
if ( ! mnodeman . Has ( vote . vinMasternode ) ) {
LogPrint ( " instantsend " , " ProcessTxLockVote -- Unknown masternode %s \n " , vote . vinMasternode . prevout . ToStringShort ( ) ) ;
return false ;
}
int n = mnodeman . GetMasternodeRank ( vote . vinMasternode , vote . nBlockHeight , MIN_INSTANTSEND_PROTO_VERSION ) ;
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-11-21 21:40:32 +01:00
LogPrint ( " instantsend " , " ProcessTxLockVote -- Outdated masternode %s \n " , vote . vinMasternode . prevout . ToStringShort ( ) ) ;
2016-11-22 15:54:26 +01:00
if ( pnode ) {
mnodeman . AskForMN ( pnode , vote . vinMasternode ) ;
}
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-11-17 01:31:35 +01:00
LogPrint ( " instantsend " , " ProcessTxLockVote -- Masternode %s, rank=%d \n " , vote . vinMasternode . prevout . ToStringShort ( ) , n ) ;
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
if ( n > INSTANTSEND_SIGNATURES_TOTAL ) {
2016-11-21 21:40:32 +01:00
LogPrint ( " instantsend " , " ProcessTxLockVote -- Masternode %s is not in the top %d (%d), vote hash=%s \n " ,
2016-10-22 18:52:14 +02:00
vote . vinMasternode . prevout . ToStringShort ( ) , 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-11-17 01:31:35 +01:00
LogPrintf ( " ProcessTxLockVote -- Signature invalid \n " ) ;
2015-08-07 05:07:40 +02:00
// don't ban, it could just be a non-synced masternode
2016-11-22 15:54:26 +01:00
if ( pnode ) {
mnodeman . AskForMN ( pnode , vote . vinMasternode ) ;
}
2015-02-02 15:36:00 +01:00
return false ;
2014-12-09 02:17:57 +01:00
}
2016-11-22 15:54:26 +01:00
if ( ! mapTxLockCandidates . count ( vote . txHash ) ) {
// this should never happen
return false ;
2016-08-05 21:49:45 +02:00
}
2015-02-01 16:53:49 +01:00
2014-12-09 02:17:57 +01:00
//compile consessus vote
2016-11-22 15:54:26 +01:00
mapTxLockCandidates [ vote . txHash ] . AddVote ( vote ) ;
int nSignatures = mapTxLockCandidates [ vote . txHash ] . CountVotes ( ) ;
LogPrint ( " instantsend " , " ProcessTxLockVote -- Transaction Lock signatures count: %d, vote hash=%s \n " , nSignatures , vote . GetHash ( ) . ToString ( ) ) ;
if ( nSignatures > = INSTANTSEND_SIGNATURES_REQUIRED ) {
LogPrint ( " instantsend " , " ProcessTxLockVote -- Transaction Lock Is Complete! txid=%s \n " , vote . txHash . ToString ( ) ) ;
if ( ! FindConflictingLocks ( mapLockRequestAccepted [ vote . txHash ] ) ) { //?????
if ( mapLockRequestAccepted . count ( vote . txHash ) ) {
UpdateLockedTransaction ( mapLockRequestAccepted [ vote . txHash ] ) ;
LockTransactionInputs ( mapLockRequestAccepted [ vote . txHash ] ) ;
} else if ( mapLockRequestRejected . count ( vote . txHash ) ) {
ResolveConflicts ( mapLockRequestRejected [ vote . txHash ] ) ; ///?????
} else {
LogPrint ( " instantsend " , " ProcessTxLockVote -- Transaction Lock is missing! nSignatures=%d, vote hash=%s \n " , nSignatures , vote . GetHash ( ) . ToString ( ) ) ;
2015-02-03 23:40:00 +01:00
}
2014-12-09 02:17:57 +01:00
}
}
2016-11-22 15:54:26 +01:00
CInv inv ( MSG_TXLOCK_VOTE , vote . GetHash ( ) ) ;
RelayInv ( inv ) ;
2015-02-01 16:53:49 +01:00
2016-11-22 15:54:26 +01:00
return true ;
}
void ProcessOrphanTxLockVotes ( )
{
2017-01-18 07:38:13 +01:00
LOCK2 ( cs_main , cs_instantsend ) ;
2016-11-22 15:54:26 +01:00
std : : map < uint256 , CTxLockVote > : : iterator it = mapTxLockVotesOrphan . begin ( ) ;
while ( it ! = mapTxLockVotesOrphan . end ( ) ) {
if ( ProcessTxLockVote ( NULL , it - > second ) ) {
mapTxLockVotesOrphan . erase ( it + + ) ;
} else {
+ + it ;
}
}
2014-12-09 02:17:57 +01:00
}
2016-11-21 21:40:32 +01:00
void UpdateLockedTransaction ( const CTransaction & tx , bool fForceNotification )
2016-08-05 21:49:45 +02:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
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-11-17 01:31:35 +01:00
if ( ! mapLockRequestAccepted . 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
}
2016-11-21 21:40:32 +01:00
void LockTransactionInputs ( const CTransaction & tx ) {
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-17 01:31:35 +01:00
if ( ! mapLockRequestAccepted . count ( tx . GetHash ( ) ) ) return ;
2016-08-05 21:49:45 +02:00
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
}
2016-11-21 21:40:32 +01:00
bool FindConflictingLocks ( const CTransaction & tx )
2015-02-05 17:48:57 +01:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
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-11-21 21:40:32 +01:00
uint256 txHash = tx . GetHash ( ) ;
2016-08-05 21:49:45 +02:00
BOOST_FOREACH ( const CTxIn & txin , tx . vin ) {
if ( mapLockedInputs . count ( txin . prevout ) ) {
2016-11-21 21:40:32 +01:00
if ( mapLockedInputs [ txin . prevout ] ! = txHash ) {
LogPrintf ( " FindConflictingLocks -- found two complete conflicting Transaction Locks, removing both: txid=%s, txin=%s " , txHash . ToString ( ) , mapLockedInputs [ txin . prevout ] . ToString ( ) ) ;
2016-08-05 21:49:45 +02:00
2016-11-21 21:40:32 +01:00
if ( mapTxLockCandidates . count ( txHash ) )
mapTxLockCandidates [ txHash ] . nExpirationBlock = - 1 ;
2016-08-05 21:49:45 +02:00
2016-11-17 01:31:35 +01:00
if ( mapTxLockCandidates . count ( mapLockedInputs [ txin . prevout ] ) )
mapTxLockCandidates [ mapLockedInputs [ txin . prevout ] ] . nExpirationBlock = - 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-11-21 21:40:32 +01:00
void ResolveConflicts ( const CTransaction & tx )
2016-08-05 21:49:45 +02:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-21 21:40:32 +01:00
uint256 txHash = tx . GetHash ( ) ;
2016-03-24 16:54:29 +01:00
// resolve conflicts
2016-11-21 21:40:32 +01:00
if ( IsLockedInstandSendTransaction ( txHash ) & & ! FindConflictingLocks ( tx ) ) { //?????
2016-10-22 18:52:14 +02:00
LogPrintf ( " ResolveConflicts -- Found existing complete Transaction 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-11-21 21:40:32 +01:00
if ( ! mapLockRequestAccepted . count ( txHash ) )
mapLockRequestAccepted . insert ( std : : make_pair ( txHash , tx ) ) ; //?????
2016-03-24 16:54:29 +01:00
}
}
2016-11-22 15:54:26 +01:00
int64_t GetAverageMasternodeOrphanVoteTime ( )
2015-02-02 18:33:52 +01:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-22 15:54:26 +01:00
// NOTE: should never actually call this function when mapMasternodeOrphanVotes is empty
if ( mapMasternodeOrphanVotes . empty ( ) ) return 0 ;
2016-11-21 21:40:32 +01:00
2016-11-22 15:54:26 +01:00
std : : map < COutPoint , int64_t > : : iterator it = mapMasternodeOrphanVotes . begin ( ) ;
2015-02-02 18:33:52 +01:00
int64_t total = 0 ;
2016-11-22 15:54:26 +01:00
while ( it ! = mapMasternodeOrphanVotes . end ( ) ) {
2015-02-02 18:33:52 +01:00
total + = it - > second ;
2016-11-21 21:40:32 +01:00
+ + it ;
2015-02-02 18:33:52 +01:00
}
2016-11-22 15:54:26 +01:00
return total / mapMasternodeOrphanVotes . size ( ) ;
2015-02-02 18:33:52 +01:00
}
2016-11-17 01:31:35 +01:00
void CleanTxLockCandidates ( )
2014-12-09 02:17:57 +01:00
{
2016-11-21 21:40:32 +01:00
int nHeight ;
{
LOCK ( cs_main ) ;
nHeight = chainActive . Height ( ) ;
}
2016-11-28 15:21:50 +01:00
LOCK ( cs_instantsend ) ;
std : : map < uint256 , CTxLockCandidate > : : iterator it = mapTxLockCandidates . begin ( ) ;
2016-11-17 01:31:35 +01:00
while ( it ! = mapTxLockCandidates . end ( ) ) {
CTxLockCandidate & txLockCandidate = it - > second ;
if ( nHeight > txLockCandidate . nExpirationBlock ) {
2016-11-21 21:40:32 +01:00
LogPrintf ( " CleanTxLockCandidates -- Removing expired Transaction Lock Candidate: txid=%s \n " , txLockCandidate . txHash . ToString ( ) ) ;
2015-02-04 21:20:13 +01:00
2016-11-17 01:31:35 +01:00
if ( mapLockRequestAccepted . count ( txLockCandidate . txHash ) ) {
CTransaction & tx = mapLockRequestAccepted [ txLockCandidate . 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-11-17 01:31:35 +01:00
mapLockRequestAccepted . erase ( txLockCandidate . txHash ) ;
mapLockRequestRejected . erase ( txLockCandidate . txHash ) ;
2015-02-06 20:58:03 +01:00
2016-11-17 01:31:35 +01:00
BOOST_FOREACH ( const CTxLockVote & vote , txLockCandidate . vecTxLockVotes )
if ( mapTxLockVotes . count ( vote . GetHash ( ) ) )
mapTxLockVotes . erase ( vote . GetHash ( ) ) ;
2015-02-04 21:20:13 +01:00
}
2016-11-17 01:31:35 +01:00
mapTxLockCandidates . erase ( it + + ) ;
2014-12-09 02:17:57 +01:00
} else {
2016-11-21 21:40:32 +01:00
+ + it ;
2014-12-09 02:17:57 +01:00
}
}
2016-11-22 15:54:26 +01:00
// clean expired orphan votes
std : : map < uint256 , CTxLockVote > : : iterator it1 = mapTxLockVotesOrphan . begin ( ) ;
while ( it1 ! = mapTxLockVotesOrphan . end ( ) ) {
if ( it1 - > second . nOrphanExpireTime < GetTime ( ) ) {
LogPrint ( " instantsend " , " CleanTxLockCandidates -- Removing expired orphan vote: txid=%s masternode=%s \n " , it1 - > second . txHash . ToString ( ) , it1 - > second . vinMasternode . prevout . ToStringShort ( ) ) ;
mapTxLockVotesOrphan . erase ( it1 + + ) ;
} else {
+ + it1 ;
}
}
// clean expired masternode orphan vote times
std : : map < COutPoint , int64_t > : : iterator it2 = mapMasternodeOrphanVotes . begin ( ) ;
while ( it2 ! = mapMasternodeOrphanVotes . end ( ) ) {
if ( it2 - > second < GetTime ( ) ) {
LogPrint ( " instantsend " , " CleanTxLockCandidates -- Removing expired orphan masternode vote time: masternode=%s \n " , it2 - > first . ToStringShort ( ) ) ;
mapMasternodeOrphanVotes . erase ( it2 + + ) ;
} else {
+ + it2 ;
}
}
2014-12-09 02:17:57 +01:00
}
2016-11-21 21:40:32 +01:00
bool IsLockedInstandSendTransaction ( const uint256 & txHash )
2016-08-05 21:49:45 +02:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-03-24 16:54:29 +01:00
// there must be a successfully verified lock request...
2016-11-17 01:31:35 +01:00
if ( ! mapLockRequestAccepted . count ( txHash ) ) return false ;
2016-03-24 16:54:29 +01:00
// ...and corresponding lock must have enough signatures
2016-11-21 21:40:32 +01:00
return GetTransactionLockSignatures ( txHash ) > = INSTANTSEND_SIGNATURES_REQUIRED ;
2016-03-23 15:49:35 +01:00
}
2016-11-21 21:40:32 +01:00
int GetTransactionLockSignatures ( const uint256 & txHash )
2016-03-23 15:49:35 +01:00
{
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
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-17 01:31:35 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator it = mapTxLockCandidates . find ( txHash ) ;
if ( it ! = mapTxLockCandidates . end ( ) ) return it - > second . CountVotes ( ) ;
2016-03-23 15:49:35 +01:00
return - 1 ;
}
2016-11-21 21:40:32 +01:00
bool IsTransactionLockTimedOut ( const uint256 & txHash )
2016-03-23 15:49:35 +01:00
{
2016-05-25 07:25:16 +02:00
if ( ! fEnableInstantSend ) return 0 ;
2016-03-23 15:49:35 +01:00
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-17 01:31:35 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator i = mapTxLockCandidates . find ( txHash ) ;
if ( i ! = mapTxLockCandidates . end ( ) ) return GetTime ( ) > ( * i ) . second . nTimeout ;
2016-03-23 15:49:35 +01:00
return false ;
}
2016-11-17 01:31:35 +01:00
uint256 CTxLockVote : : GetHash ( ) const
2015-02-01 16:53:49 +01:00
{
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-11-21 21:40:32 +01:00
bool CTxLockVote : : CheckSignature ( ) const
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 ) ;
2014-12-31 03:54:00 +01:00
2016-11-21 21:40:32 +01:00
masternode_info_t infoMn = mnodeman . GetMasternodeInfo ( vinMasternode ) ;
2014-12-09 02:17:57 +01:00
2016-11-21 21:40:32 +01:00
if ( ! infoMn . fInfoValid ) {
2016-11-17 01:31:35 +01:00
LogPrintf ( " CTxLockVote::CheckSignature -- Unknown Masternode: txin=%s \n " , vinMasternode . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2016-11-21 21:40:32 +01:00
if ( ! darkSendSigner . VerifyMessage ( infoMn . pubKeyMasternode , vchMasterNodeSignature , strMessage , strError ) ) {
2016-11-17 01:31:35 +01:00
LogPrintf ( " CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s \n " , strError ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
return true ;
}
2016-11-17 01:31:35 +01:00
bool CTxLockVote : : Sign ( )
2014-12-09 02:17:57 +01:00
{
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 ) ) {
2016-11-17 01:31:35 +01:00
LogPrintf ( " CTxLockVote::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-11-17 01:31:35 +01:00
LogPrintf ( " CTxLockVote::Sign -- VerifyMessage() failed, error: %s \n " , strError ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
return true ;
}
2016-11-17 01:31:35 +01:00
bool CTxLockCandidate : : IsAllVotesValid ( )
2014-12-09 02:17:57 +01:00
{
2016-11-21 21:40:32 +01:00
BOOST_FOREACH ( const CTxLockVote & vote , vecTxLockVotes )
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
if ( n = = - 1 ) {
2016-11-17 01:31:35 +01:00
LogPrintf ( " CTxLockCandidate::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-11-17 01:31:35 +01:00
LogPrintf ( " CTxLockCandidate::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 ( ) ) {
2016-11-17 01:31:35 +01:00
LogPrintf ( " CTxLockCandidate::IsAllVotesValid -- Signature not valid \n " ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
}
return true ;
}
2016-11-21 21:40:32 +01:00
void CTxLockCandidate : : AddVote ( const CTxLockVote & vote )
2014-12-09 02:17:57 +01:00
{
2016-11-17 01:31:35 +01:00
vecTxLockVotes . push_back ( vote ) ;
2014-12-09 02:17:57 +01:00
}
2016-11-17 01:31:35 +01:00
int CTxLockCandidate : : 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 ;
2016-11-21 21:40:32 +01:00
BOOST_FOREACH ( const CTxLockVote & vote , vecTxLockVotes )
2016-08-05 21:49:45 +02:00
if ( vote . nBlockHeight = = nBlockHeight )
nCount + + ;
return nCount ;
2014-12-06 20:41:53 +01:00
}