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
# include "activemasternode.h"
2016-12-20 14:27:59 +01:00
# include "instantx.h"
# include "key.h"
2017-08-09 02:19:06 +02:00
# include "validation.h"
2016-01-24 20:05:31 +01:00
# include "masternode-sync.h"
# include "masternodeman.h"
2017-04-12 09:04:06 +02:00
# include "messagesigner.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"
2017-01-29 09:22:14 +01:00
# include "txmempool.h"
2016-12-20 14:27:59 +01:00
# include "util.h"
# include "consensus/validation.h"
2016-03-24 16:54:29 +01:00
# include <boost/algorithm/string/replace.hpp>
# include <boost/thread.hpp>
2014-12-09 02:17:57 +01:00
2016-08-05 21:49:45 +02:00
extern CWallet * pwalletMain ;
2017-01-29 09:22:14 +01:00
extern CTxMemPool mempool ;
2016-08-05 21:49:45 +02:00
bool fEnableInstantSend = true ;
int nInstantSendDepth = DEFAULT_INSTANTSEND_DEPTH ;
int nCompleteTXLocks ;
2014-12-31 03:54:00 +01:00
2017-01-29 09:22:14 +01:00
CInstantSend 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
2017-01-29 09:22:14 +01:00
// step 2) Top COutPointLock::SIGNATURES_TOTAL masternodes per each spent outpoint push "txvote" message
// step 3) Once there are COutPointLock::SIGNATURES_REQUIRED valid "txvote" messages per each spent outpoint
// for a corresponding "txlreg" message, all outpoints from that tx are treated as locked
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
//
// CInstantSend
//
2017-09-15 20:05:13 +02:00
void CInstantSend : : ProcessMessage ( CNode * pfrom , std : : string & strCommand , CDataStream & vRecv , CConnman & connman )
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
{
2017-01-29 09:22:14 +01:00
if ( pfrom - > nVersion < MIN_INSTANTSEND_PROTO_VERSION ) return ;
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-08-11 20:51:44 +02:00
LOCK ( cs_main ) ;
# ifdef ENABLE_WALLET
if ( pwalletMain )
LOCK ( pwalletMain - > cs_wallet ) ;
# endif
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
uint256 nVoteHash = vote . GetHash ( ) ;
if ( mapTxLockVotes . count ( nVoteHash ) ) return ;
mapTxLockVotes . insert ( std : : make_pair ( nVoteHash , vote ) ) ;
2014-12-31 03:54:00 +01:00
2017-09-15 20:05:13 +02:00
ProcessTxLockVote ( pfrom , vote , connman ) ;
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
}
}
2017-09-15 20:05:13 +02:00
bool CInstantSend : : ProcessTxLockRequest ( const CTxLockRequest & txLockRequest , CConnman & connman )
2016-08-05 21:49:45 +02:00
{
2017-01-29 09:22:14 +01:00
LOCK2 ( cs_main , cs_instantsend ) ;
2015-02-04 11:44:41 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockRequest . GetHash ( ) ;
2017-09-14 13:41:40 +02:00
// Check to see if we conflict with existing completed lock
2017-01-29 09:22:14 +01:00
BOOST_FOREACH ( const CTxIn & txin , txLockRequest . vin ) {
std : : map < COutPoint , uint256 > : : iterator it = mapLockedOutpoints . find ( txin . prevout ) ;
2017-09-14 13:41:40 +02:00
if ( it ! = mapLockedOutpoints . end ( ) & & it - > second ! = txLockRequest . GetHash ( ) ) {
// Conflicting with complete lock, proceed to see if we should cancel them both
LogPrintf ( " CInstantSend::ProcessTxLockRequest -- WARNING: Found conflicting completed Transaction Lock, txid=%s, completed lock txid=%s \n " ,
2017-01-29 09:22:14 +01:00
txLockRequest . GetHash ( ) . ToString ( ) , it - > second . ToString ( ) ) ;
2016-12-08 21:03:57 +01:00
}
2016-09-05 18:09:25 +02:00
}
2015-02-04 11:44:41 +01:00
2017-01-29 09:22:14 +01:00
// Check to see if there are votes for conflicting request,
// if so - do not fail, just warn user
BOOST_FOREACH ( const CTxIn & txin , txLockRequest . vin ) {
std : : map < COutPoint , std : : set < uint256 > > : : iterator it = mapVotedOutpoints . find ( txin . prevout ) ;
if ( it ! = mapVotedOutpoints . end ( ) ) {
BOOST_FOREACH ( const uint256 & hash , it - > second ) {
if ( hash ! = txLockRequest . GetHash ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockRequest -- Double spend attempt! %s \n " , txin . prevout . ToStringShort ( ) ) ;
// do not fail here, let it go and see which one will get the votes to be locked
2017-09-14 13:41:40 +02:00
// TODO: notify zmq+script
2017-01-29 09:22:14 +01:00
}
}
2015-02-04 11:44:41 +01:00
}
}
2017-01-29 09:22:14 +01:00
if ( ! CreateTxLockCandidate ( txLockRequest ) ) {
2016-11-22 15:54:26 +01:00
// smth is not right
2017-01-29 09:22:14 +01:00
LogPrintf ( " CInstantSend::ProcessTxLockRequest -- CreateTxLockCandidate failed, txid=%s \n " , txHash . ToString ( ) ) ;
2016-11-22 15:54:26 +01:00
return false ;
}
2017-01-29 09:22:14 +01:00
LogPrintf ( " CInstantSend::ProcessTxLockRequest -- accepted, txid=%s \n " , txHash . ToString ( ) ) ;
2016-11-22 15:54:26 +01:00
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
CTxLockCandidate & txLockCandidate = itLockCandidate - > second ;
2017-09-15 20:05:13 +02:00
Vote ( txLockCandidate , connman ) ;
ProcessOrphanTxLockVotes ( connman ) ;
2016-11-22 15:54:26 +01:00
// Masternodes will sometimes propagate votes before the transaction is known to the client.
2017-01-29 09:22:14 +01:00
// If this just happened - lock inputs, resolve conflicting locks, update transaction status
// forcing external script notification.
TryToFinalizeLockCandidate ( txLockCandidate ) ;
2016-11-22 15:54:26 +01:00
return true ;
}
2017-01-29 09:22:14 +01:00
bool CInstantSend : : CreateTxLockCandidate ( const CTxLockRequest & txLockRequest )
2014-12-09 02:17:57 +01:00
{
2017-09-14 13:41:40 +02:00
if ( ! txLockRequest . IsValid ( ) ) return false ;
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
LOCK ( cs_instantsend ) ;
2015-02-04 22:09:50 +01:00
2017-09-14 13:41:40 +02:00
uint256 txHash = txLockRequest . GetHash ( ) ;
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate = = mapTxLockCandidates . end ( ) ) {
LogPrintf ( " CInstantSend::CreateTxLockCandidate -- new, txid=%s \n " , txHash . ToString ( ) ) ;
2016-11-21 21:40:32 +01:00
2017-01-29 09:22:14 +01:00
CTxLockCandidate txLockCandidate ( txLockRequest ) ;
// all inputs should already be checked by txLockRequest.IsValid() above, just use them now
BOOST_REVERSE_FOREACH ( const CTxIn & txin , txLockRequest . vin ) {
txLockCandidate . AddOutPointLock ( txin . prevout ) ;
}
2016-11-21 21:40:32 +01:00
mapTxLockCandidates . insert ( std : : make_pair ( txHash , txLockCandidate ) ) ;
2017-09-14 13:41:40 +02:00
} else if ( ! itLockCandidate - > second . txLockRequest ) {
// i.e. empty Transaction Lock Candidate was created earlier, let's update it with actual data
itLockCandidate - > second . txLockRequest = txLockRequest ;
if ( itLockCandidate - > second . IsTimedOut ( ) ) {
LogPrintf ( " CInstantSend::CreateTxLockCandidate -- timed out, txid=%s \n " , txHash . ToString ( ) ) ;
return false ;
}
LogPrintf ( " CInstantSend::CreateTxLockCandidate -- update empty, txid=%s \n " , txHash . ToString ( ) ) ;
// all inputs should already be checked by txLockRequest.IsValid() above, just use them now
BOOST_REVERSE_FOREACH ( const CTxIn & txin , txLockRequest . vin ) {
itLockCandidate - > second . AddOutPointLock ( txin . prevout ) ;
}
2015-02-04 22:09:50 +01:00
} else {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::CreateTxLockCandidate -- seen, txid=%s \n " , txHash . ToString ( ) ) ;
2015-02-04 22:09:50 +01:00
}
2017-01-29 09:22:14 +01:00
return true ;
2015-02-04 22:59:19 +01:00
}
2017-09-14 13:41:40 +02:00
void CInstantSend : : CreateEmptyTxLockCandidate ( const uint256 & txHash )
{
if ( mapTxLockCandidates . find ( txHash ) ! = mapTxLockCandidates . end ( ) )
return ;
LogPrintf ( " CInstantSend::CreateEmptyTxLockCandidate -- new, txid=%s \n " , txHash . ToString ( ) ) ;
const CTxLockRequest txLockRequest = CTxLockRequest ( ) ;
mapTxLockCandidates . insert ( std : : make_pair ( txHash , CTxLockCandidate ( txLockRequest ) ) ) ;
}
2017-09-15 20:05:13 +02:00
void CInstantSend : : Vote ( CTxLockCandidate & txLockCandidate , CConnman & connman )
2015-02-04 22:59:19 +01:00
{
if ( ! fMasterNode ) return ;
2017-09-14 13:41:40 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2015-02-04 22:59:19 +01:00
2017-01-29 09:22:14 +01:00
LOCK2 ( cs_main , cs_instantsend ) ;
2015-02-06 05:41:17 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . GetHash ( ) ;
// check if we need to vote on this candidate's outpoints,
// it's possible that we need to vote for several of them
std : : map < COutPoint , COutPointLock > : : iterator itOutpointLock = txLockCandidate . mapOutPointLocks . begin ( ) ;
while ( itOutpointLock ! = txLockCandidate . mapOutPointLocks . end ( ) ) {
2015-02-06 05:41:17 +01:00
2017-01-29 09:22:14 +01:00
int nPrevoutHeight = GetUTXOHeight ( itOutpointLock - > first ) ;
if ( nPrevoutHeight = = - 1 ) {
LogPrint ( " instantsend " , " CInstantSend::Vote -- Failed to find UTXO %s \n " , itOutpointLock - > first . ToStringShort ( ) ) ;
return ;
}
2015-02-04 22:59:19 +01:00
2017-01-29 09:22:14 +01:00
int nLockInputHeight = nPrevoutHeight + 4 ;
2015-02-06 05:41:17 +01:00
2017-09-14 15:58:29 +02:00
int nRank ;
if ( ! mnodeman . GetMasternodeRank ( activeMasternode . outpoint , nRank , nLockInputHeight , MIN_INSTANTSEND_PROTO_VERSION ) ) {
2017-09-11 16:13:48 +02:00
LogPrint ( " instantsend " , " CInstantSend::Vote -- Can't calculate rank for masternode %s \n " , activeMasternode . outpoint . ToStringShort ( ) ) ;
2017-01-29 20:09:21 +01:00
+ + itOutpointLock ;
2017-01-29 09:22:14 +01:00
continue ;
}
2015-02-06 20:07:22 +01:00
2017-01-29 09:22:14 +01:00
int nSignaturesTotal = COutPointLock : : SIGNATURES_TOTAL ;
2017-09-14 15:58:29 +02:00
if ( nRank > nSignaturesTotal ) {
LogPrint ( " instantsend " , " CInstantSend::Vote -- Masternode not in the top %d (%d) \n " , nSignaturesTotal , nRank ) ;
2017-01-29 20:09:21 +01:00
+ + itOutpointLock ;
2017-01-29 09:22:14 +01:00
continue ;
}
2017-09-14 15:58:29 +02:00
LogPrint ( " instantsend " , " CInstantSend::Vote -- In the top %d (%d) \n " , nSignaturesTotal , nRank ) ;
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , std : : set < uint256 > > : : iterator itVoted = mapVotedOutpoints . find ( itOutpointLock - > first ) ;
// Check to see if we already voted for this outpoint,
// refuse to vote twice or to include the same outpoint in another tx
bool fAlreadyVoted = false ;
if ( itVoted ! = mapVotedOutpoints . end ( ) ) {
BOOST_FOREACH ( const uint256 & hash , itVoted - > second ) {
std : : map < uint256 , CTxLockCandidate > : : iterator it2 = mapTxLockCandidates . find ( hash ) ;
2017-09-11 16:13:48 +02:00
if ( it2 - > second . HasMasternodeVoted ( itOutpointLock - > first , activeMasternode . outpoint ) ) {
2017-01-29 09:22:14 +01:00
// we already voted for this outpoint to be included either in the same tx or in a competing one,
// skip it anyway
fAlreadyVoted = true ;
LogPrintf ( " CInstantSend::Vote -- WARNING: We already voted for this outpoint, skipping: txHash=%s, outpoint=%s \n " ,
txHash . ToString ( ) , itOutpointLock - > first . ToStringShort ( ) ) ;
break ;
}
}
}
2017-01-29 20:09:21 +01:00
if ( fAlreadyVoted ) {
+ + itOutpointLock ;
continue ; // skip to the next outpoint
}
2017-01-29 09:22:14 +01:00
// we haven't voted for this outpoint yet, let's try to do this now
2017-09-11 16:13:48 +02:00
CTxLockVote vote ( txHash , itOutpointLock - > first , activeMasternode . outpoint ) ;
2017-01-29 09:22:14 +01:00
if ( ! vote . Sign ( ) ) {
LogPrintf ( " CInstantSend::Vote -- Failed to sign consensus vote \n " ) ;
return ;
}
if ( ! vote . CheckSignature ( ) ) {
LogPrintf ( " CInstantSend::Vote -- Signature invalid \n " ) ;
return ;
}
// vote constructed sucessfully, let's store and relay it
uint256 nVoteHash = vote . GetHash ( ) ;
mapTxLockVotes . insert ( std : : make_pair ( nVoteHash , vote ) ) ;
if ( itOutpointLock - > second . AddVote ( vote ) ) {
LogPrintf ( " CInstantSend::Vote -- Vote created successfully, relaying: txHash=%s, outpoint=%s, vote=%s \n " ,
txHash . ToString ( ) , itOutpointLock - > first . ToStringShort ( ) , nVoteHash . ToString ( ) ) ;
if ( itVoted = = mapVotedOutpoints . end ( ) ) {
std : : set < uint256 > setHashes ;
setHashes . insert ( txHash ) ;
mapVotedOutpoints . insert ( std : : make_pair ( itOutpointLock - > first , setHashes ) ) ;
} else {
mapVotedOutpoints [ itOutpointLock - > first ] . insert ( txHash ) ;
if ( mapVotedOutpoints [ itOutpointLock - > first ] . size ( ) > 1 ) {
// it's ok to continue, just warn user
LogPrintf ( " CInstantSend::Vote -- WARNING: Vote conflicts with some existing votes: txHash=%s, outpoint=%s, vote=%s \n " ,
txHash . ToString ( ) , itOutpointLock - > first . ToStringShort ( ) , nVoteHash . ToString ( ) ) ;
}
}
2017-09-15 20:05:13 +02:00
vote . Relay ( connman ) ;
2017-01-29 09:22:14 +01:00
}
+ + itOutpointLock ;
}
2014-12-09 02:17:57 +01:00
}
//received a consensus vote
2017-09-15 20:05:13 +02:00
bool CInstantSend : : ProcessTxLockVote ( CNode * pfrom , CTxLockVote & vote , CConnman & connman )
2014-12-09 02:17:57 +01:00
{
2017-08-11 20:51:44 +02:00
// cs_main, cs_wallet and cs_instantsend should be already locked
AssertLockHeld ( cs_main ) ;
# ifdef ENABLE_WALLET
if ( pwalletMain )
AssertLockHeld ( pwalletMain - > cs_wallet ) ;
# endif
AssertLockHeld ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
uint256 txHash = vote . GetTxHash ( ) ;
if ( ! vote . IsValid ( pfrom ) ) {
// could be because of missing MN
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- Vote is invalid, txid=%s \n " , txHash . ToString ( ) ) ;
return false ;
}
2017-09-14 13:41:40 +02:00
// relay valid vote asap
2017-09-15 20:05:13 +02:00
vote . Relay ( connman ) ;
2017-09-14 13:41:40 +02:00
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
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator it = mapTxLockCandidates . find ( txHash ) ;
2017-09-14 13:41:40 +02:00
if ( it = = mapTxLockCandidates . end ( ) | | ! it - > second . txLockRequest ) {
2016-11-22 15:54:26 +01:00
if ( ! mapTxLockVotesOrphan . count ( vote . GetHash ( ) ) ) {
2017-09-14 13:41:40 +02:00
// start timeout countdown after the very first vote
CreateEmptyTxLockCandidate ( txHash ) ;
2016-11-22 15:54:26 +01:00
mapTxLockVotesOrphan [ vote . GetHash ( ) ] = vote ;
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- Orphan vote: txid=%s masternode=%s new \n " ,
txHash . ToString ( ) , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
bool fReprocess = true ;
std : : map < uint256 , CTxLockRequest > : : iterator itLockRequest = mapLockRequestAccepted . find ( txHash ) ;
if ( itLockRequest = = mapLockRequestAccepted . end ( ) ) {
itLockRequest = mapLockRequestRejected . find ( txHash ) ;
if ( itLockRequest = = mapLockRequestRejected . end ( ) ) {
// still too early, wait for tx lock request
fReprocess = false ;
}
}
if ( fReprocess & & IsEnoughOrphanVotesForTx ( itLockRequest - > second ) ) {
// We have enough votes for corresponding lock to complete,
// tx lock request should already be received at this stage.
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- Found enough orphan votes, reprocessing Transaction Lock Request: txid=%s \n " , txHash . ToString ( ) ) ;
2017-09-15 20:05:13 +02:00
ProcessTxLockRequest ( itLockRequest - > second , connman ) ;
2017-01-29 09:22:14 +01:00
return true ;
}
2016-11-22 15:54:26 +01:00
} else {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- Orphan vote: txid=%s masternode=%s seen \n " ,
txHash . ToString ( ) , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
2016-11-22 15:54:26 +01:00
}
// This tracks those messages and allows only the same rate as of the rest of the network
2017-01-29 09:22:14 +01:00
// TODO: make sure this works good enough for multi-quorum
2016-11-22 15:54:26 +01:00
int nMasternodeOrphanExpireTime = GetTime ( ) + 60 * 10 ; // keep time data for 10 minutes
2017-01-29 09:22:14 +01:00
if ( ! mapMasternodeOrphanVotes . count ( vote . GetMasternodeOutpoint ( ) ) ) {
mapMasternodeOrphanVotes [ vote . GetMasternodeOutpoint ( ) ] = nMasternodeOrphanExpireTime ;
2016-11-22 15:54:26 +01:00
} else {
2017-01-29 09:22:14 +01:00
int64_t nPrevOrphanVote = mapMasternodeOrphanVotes [ vote . GetMasternodeOutpoint ( ) ] ;
2016-11-22 15:54:26 +01:00
if ( nPrevOrphanVote > GetTime ( ) & & nPrevOrphanVote > GetAverageMasternodeOrphanVoteTime ( ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- masternode is spamming orphan Transaction Lock Votes: txid=%s masternode=%s \n " ,
txHash . ToString ( ) , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
2016-11-22 15:54:26 +01:00
// Misbehaving(pfrom->id, 1);
return false ;
}
// not spamming, refresh
2017-01-29 09:22:14 +01:00
mapMasternodeOrphanVotes [ vote . GetMasternodeOutpoint ( ) ] = nMasternodeOrphanExpireTime ;
2016-11-22 15:54:26 +01:00
}
return true ;
}
2014-12-09 02:17:57 +01:00
2017-09-14 13:41:40 +02:00
CTxLockCandidate & txLockCandidate = it - > second ;
if ( txLockCandidate . IsTimedOut ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- too late, Transaction Lock timed out, txid=%s \n " , txHash . ToString ( ) ) ;
return false ;
}
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- Transaction Lock Vote, txid=%s \n " , txHash . ToString ( ) ) ;
std : : map < COutPoint , std : : set < uint256 > > : : iterator it1 = mapVotedOutpoints . find ( vote . GetOutpoint ( ) ) ;
if ( it1 ! = mapVotedOutpoints . end ( ) ) {
BOOST_FOREACH ( const uint256 & hash , it1 - > second ) {
if ( hash ! = txHash ) {
// same outpoint was already voted to be locked by another tx lock request,
2017-09-14 13:41:40 +02:00
// let's see if it was the same masternode who voted on this outpoint
// for another tx lock request
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator it2 = mapTxLockCandidates . find ( hash ) ;
2017-09-14 13:41:40 +02:00
if ( it2 ! = mapTxLockCandidates . end ( ) & & it2 - > second . HasMasternodeVoted ( vote . GetOutpoint ( ) , vote . GetMasternodeOutpoint ( ) ) ) {
// yes, it was the same masternode
2017-01-29 09:22:14 +01:00
LogPrintf ( " CInstantSend::ProcessTxLockVote -- masternode sent conflicting votes! %s \n " , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
2017-09-14 13:41:40 +02:00
// mark both Lock Candidates as attacked, none of them should complete,
// or at least the new (current) one shouldn't even
// if the second one was already completed earlier
txLockCandidate . MarkOutpointAsAttacked ( vote . GetOutpoint ( ) ) ;
it2 - > second . MarkOutpointAsAttacked ( vote . GetOutpoint ( ) ) ;
// apply maximum PoSe ban score to this masternode i.e. PoSe-ban it instantly
mnodeman . PoSeBan ( vote . GetMasternodeOutpoint ( ) ) ;
// NOTE: This vote must be relayed further to let all other nodes know about such
// misbehaviour of this masternode. This way they should also be able to construct
// conflicting lock and PoSe-ban this masternode.
2017-01-29 09:22:14 +01:00
}
}
2016-11-22 15:54:26 +01:00
}
2017-09-14 13:41:40 +02:00
// store all votes, regardless of them being sent by malicious masternode or not
2017-01-29 09:22:14 +01:00
it1 - > second . insert ( txHash ) ;
} else {
std : : set < uint256 > setHashes ;
setHashes . insert ( txHash ) ;
mapVotedOutpoints . insert ( std : : make_pair ( vote . GetOutpoint ( ) , setHashes ) ) ;
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
if ( ! txLockCandidate . AddVote ( vote ) ) {
2016-11-22 15:54:26 +01:00
// this should never happen
return false ;
2016-08-05 21:49:45 +02:00
}
2015-02-01 16:53:49 +01:00
2017-01-29 09:22:14 +01:00
int nSignatures = txLockCandidate . CountVotes ( ) ;
int nSignaturesMax = txLockCandidate . txLockRequest . GetMaxSignatures ( ) ;
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockVote -- Transaction Lock signatures count: %d/%d, vote hash=%s \n " ,
nSignatures , nSignaturesMax , vote . GetHash ( ) . ToString ( ) ) ;
2016-11-22 15:54:26 +01:00
2017-01-29 09:22:14 +01:00
TryToFinalizeLockCandidate ( txLockCandidate ) ;
2014-12-09 02:17:57 +01:00
2016-11-22 15:54:26 +01:00
return true ;
}
2017-09-15 20:05:13 +02:00
void CInstantSend : : ProcessOrphanTxLockVotes ( CConnman & connman )
2016-11-22 15:54:26 +01:00
{
2017-08-11 20:51:44 +02:00
LOCK ( cs_main ) ;
# ifdef ENABLE_WALLET
if ( pwalletMain )
LOCK ( pwalletMain - > cs_wallet ) ;
# endif
LOCK ( cs_instantsend ) ;
2016-11-22 15:54:26 +01:00
std : : map < uint256 , CTxLockVote > : : iterator it = mapTxLockVotesOrphan . begin ( ) ;
while ( it ! = mapTxLockVotesOrphan . end ( ) ) {
2017-09-15 20:05:13 +02:00
if ( ProcessTxLockVote ( NULL , it - > second , connman ) ) {
2016-11-22 15:54:26 +01:00
mapTxLockVotesOrphan . erase ( it + + ) ;
} else {
+ + it ;
}
}
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
bool CInstantSend : : IsEnoughOrphanVotesForTx ( const CTxLockRequest & txLockRequest )
{
// There could be a situation when we already have quite a lot of votes
// but tx lock request still wasn't received. Let's scan through
// orphan votes to check if this is the case.
BOOST_FOREACH ( const CTxIn & txin , txLockRequest . vin ) {
if ( ! IsEnoughOrphanVotesForTxAndOutPoint ( txLockRequest . GetHash ( ) , txin . prevout ) ) {
return false ;
}
}
return true ;
}
bool CInstantSend : : IsEnoughOrphanVotesForTxAndOutPoint ( const uint256 & txHash , const COutPoint & outpoint )
{
// Scan orphan votes to check if this outpoint has enough orphan votes to be locked in some tx.
LOCK2 ( cs_main , cs_instantsend ) ;
int nCountVotes = 0 ;
std : : map < uint256 , CTxLockVote > : : iterator it = mapTxLockVotesOrphan . begin ( ) ;
while ( it ! = mapTxLockVotesOrphan . end ( ) ) {
if ( it - > second . GetTxHash ( ) = = txHash & & it - > second . GetOutpoint ( ) = = outpoint ) {
nCountVotes + + ;
if ( nCountVotes > = COutPointLock : : SIGNATURES_REQUIRED ) {
return true ;
}
}
+ + it ;
}
return false ;
}
void CInstantSend : : TryToFinalizeLockCandidate ( const CTxLockCandidate & txLockCandidate )
{
2017-09-14 13:41:40 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2017-08-11 20:51:44 +02:00
LOCK ( cs_main ) ;
# ifdef ENABLE_WALLET
if ( pwalletMain )
LOCK ( pwalletMain - > cs_wallet ) ;
# endif
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . txLockRequest . GetHash ( ) ;
if ( txLockCandidate . IsAllOutPointsReady ( ) & & ! IsLockedInstantSendTransaction ( txHash ) ) {
// we have enough votes now
LogPrint ( " instantsend " , " CInstantSend::TryToFinalizeLockCandidate -- Transaction Lock is ready to complete, txid=%s \n " , txHash . ToString ( ) ) ;
2017-09-14 13:41:40 +02:00
if ( ResolveConflicts ( txLockCandidate ) ) {
2017-01-29 09:22:14 +01:00
LockTransactionInputs ( txLockCandidate ) ;
UpdateLockedTransaction ( txLockCandidate ) ;
}
}
}
void CInstantSend : : UpdateLockedTransaction ( const CTxLockCandidate & txLockCandidate )
2016-08-05 21:49:45 +02:00
{
2017-08-11 20:51:44 +02:00
// cs_wallet and cs_instantsend should be already locked
# ifdef ENABLE_WALLET
if ( pwalletMain )
AssertLockHeld ( pwalletMain - > cs_wallet ) ;
# endif
AssertLockHeld ( cs_instantsend ) ;
2016-03-24 16:54:29 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . GetHash ( ) ;
if ( ! IsLockedInstantSendTransaction ( txHash ) ) return ; // not a locked tx, do not update/notify
2016-07-15 08:38:33 +02:00
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 + + ;
2017-01-29 09:22:14 +01:00
// notify an external script once threshold is reached
std : : string strCmd = GetArg ( " -instantsendnotify " , " " ) ;
if ( ! strCmd . empty ( ) ) {
boost : : replace_all ( strCmd , " %s " , txHash . GetHex ( ) ) ;
boost : : thread t ( runCommand , strCmd ) ; // thread runs free
2016-03-24 16:54:29 +01:00
}
}
# endif
2016-07-15 08:38:33 +02:00
2017-01-29 09:22:14 +01:00
GetMainSignals ( ) . NotifyTransactionLock ( txLockCandidate . txLockRequest ) ;
2016-03-24 16:54:29 +01:00
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::UpdateLockedTransaction -- done, txid=%s \n " , txHash . ToString ( ) ) ;
2016-03-24 16:54:29 +01:00
}
2017-01-29 09:22:14 +01:00
void CInstantSend : : LockTransactionInputs ( const CTxLockCandidate & txLockCandidate )
2015-02-05 17:48:57 +01:00
{
2017-09-14 13:41:40 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2015-02-05 17:48:57 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . GetHash ( ) ;
2016-08-05 21:49:45 +02:00
2017-01-29 09:22:14 +01:00
if ( ! txLockCandidate . IsAllOutPointsReady ( ) ) return ;
2016-08-05 21:49:45 +02:00
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , COutPointLock > : : const_iterator it = txLockCandidate . mapOutPointLocks . begin ( ) ;
2016-08-05 21:49:45 +02:00
2017-01-29 09:22:14 +01:00
while ( it ! = txLockCandidate . mapOutPointLocks . end ( ) ) {
mapLockedOutpoints . insert ( std : : make_pair ( it - > first , txHash ) ) ;
+ + it ;
2015-02-05 17:48:57 +01:00
}
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::LockTransactionInputs -- done, txid=%s \n " , txHash . ToString ( ) ) ;
2015-02-05 17:48:57 +01:00
}
2015-02-02 18:33:52 +01:00
2017-01-29 09:22:14 +01:00
bool CInstantSend : : GetLockedOutPointTxHash ( const COutPoint & outpoint , uint256 & hashRet )
2016-08-05 21:49:45 +02:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , uint256 > : : iterator it = mapLockedOutpoints . find ( outpoint ) ;
if ( it = = mapLockedOutpoints . end ( ) ) return false ;
hashRet = it - > second ;
return true ;
}
2017-09-14 13:41:40 +02:00
bool CInstantSend : : ResolveConflicts ( const CTxLockCandidate & txLockCandidate )
2017-01-29 09:22:14 +01:00
{
LOCK2 ( cs_main , cs_instantsend ) ;
2016-03-24 16:54:29 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . GetHash ( ) ;
// make sure the lock is ready
2017-09-14 13:41:40 +02:00
if ( ! txLockCandidate . IsAllOutPointsReady ( ) ) return false ;
2017-01-29 09:22:14 +01:00
2017-09-14 13:41:40 +02:00
LOCK ( mempool . cs ) ; // protect mempool.mapNextTx
2017-01-29 09:22:14 +01:00
BOOST_FOREACH ( const CTxIn & txin , txLockCandidate . txLockRequest . vin ) {
uint256 hashConflicting ;
if ( GetLockedOutPointTxHash ( txin . prevout , hashConflicting ) & & txHash ! = hashConflicting ) {
2017-09-14 13:41:40 +02:00
// completed lock which conflicts with another completed one?
// this means that majority of MNs in the quorum for this specific tx input are malicious!
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidateConflicting = mapTxLockCandidates . find ( hashConflicting ) ;
if ( itLockCandidate = = mapTxLockCandidates . end ( ) | | itLockCandidateConflicting = = mapTxLockCandidates . end ( ) ) {
// safety check, should never really happen
LogPrintf ( " CInstantSend::ResolveConflicts -- ERROR: Found conflicting completed Transaction Lock, but one of txLockCandidate-s is missing, txid=%s, conflicting txid=%s \n " ,
txHash . ToString ( ) , hashConflicting . ToString ( ) ) ;
return false ;
}
LogPrintf ( " CInstantSend::ResolveConflicts -- WARNING: Found conflicting completed Transaction Lock, dropping both, txid=%s, conflicting txid=%s \n " ,
2017-01-29 09:22:14 +01:00
txHash . ToString ( ) , hashConflicting . ToString ( ) ) ;
2017-09-14 13:41:40 +02:00
CTxLockRequest txLockRequest = itLockCandidate - > second . txLockRequest ;
CTxLockRequest txLockRequestConflicting = itLockCandidateConflicting - > second . txLockRequest ;
itLockCandidate - > second . SetConfirmedHeight ( 0 ) ; // expired
itLockCandidateConflicting - > second . SetConfirmedHeight ( 0 ) ; // expired
CheckAndRemove ( ) ; // clean up
// AlreadyHave should still return "true" for both of them
mapLockRequestRejected . insert ( make_pair ( txHash , txLockRequest ) ) ;
mapLockRequestRejected . insert ( make_pair ( hashConflicting , txLockRequestConflicting ) ) ;
// TODO: clean up mapLockRequestRejected later somehow
// (not a big issue since we already PoSe ban malicious masternodes
// and they won't be able to spam)
// TODO: ban all malicious masternodes permanently, do not accept anything from them, ever
// TODO: notify zmq+script about this double-spend attempt
// and let merchant cancel/hold the order if it's not too late...
// can't do anything else, fallback to regular txes
return false ;
2017-01-29 09:22:14 +01:00
} else if ( mempool . mapNextTx . count ( txin . prevout ) ) {
// check if it's in mempool
hashConflicting = mempool . mapNextTx [ txin . prevout ] . ptx - > GetHash ( ) ;
if ( txHash = = hashConflicting ) continue ; // matches current, not a conflict, skip to next txin
2017-09-14 13:41:40 +02:00
// conflicts with tx in mempool
LogPrintf ( " CInstantSend::ResolveConflicts -- ERROR: Failed to complete Transaction Lock, conflicts with mempool, txid=%s \n " , txHash . ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-14 13:41:40 +02:00
} // FOREACH
2017-01-29 09:22:14 +01:00
// No conflicts were found so far, check to see if it was already included in block
CTransaction txTmp ;
uint256 hashBlock ;
if ( GetTransaction ( txHash , txTmp , Params ( ) . GetConsensus ( ) , hashBlock , true ) & & hashBlock ! = uint256 ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::ResolveConflicts -- Done, %s is included in block %s \n " , txHash . ToString ( ) , hashBlock . ToString ( ) ) ;
return true ;
}
// Not in block yet, make sure all its inputs are still unspent
BOOST_FOREACH ( const CTxIn & txin , txLockCandidate . txLockRequest . vin ) {
CCoins coins ;
2017-08-25 14:56:48 +02:00
if ( ! GetUTXOCoins ( txin . prevout , coins ) ) {
2017-01-29 09:22:14 +01:00
// Not in UTXO anymore? A conflicting tx was mined while we were waiting for votes.
2017-09-14 13:41:40 +02:00
LogPrintf ( " CInstantSend::ResolveConflicts -- ERROR: Failed to find UTXO %s, can't complete Transaction Lock \n " , txin . prevout . ToStringShort ( ) ) ;
return false ;
2017-01-29 09:22:14 +01:00
}
2016-03-24 16:54:29 +01:00
}
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::ResolveConflicts -- Done, txid=%s \n " , txHash . ToString ( ) ) ;
return true ;
2016-03-24 16:54:29 +01:00
}
2017-01-29 09:22:14 +01:00
int64_t CInstantSend : : 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
}
2017-01-29 09:22:14 +01:00
void CInstantSend : : CheckAndRemove ( )
2014-12-09 02:17:57 +01:00
{
2017-08-25 14:57:05 +02:00
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) return ;
2016-11-21 21:40:32 +01:00
2016-11-28 15:21:50 +01:00
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . begin ( ) ;
// remove expired candidates
while ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
CTxLockCandidate & txLockCandidate = itLockCandidate - > second ;
uint256 txHash = txLockCandidate . GetHash ( ) ;
2017-08-25 14:57:05 +02:00
if ( txLockCandidate . IsExpired ( nCachedBlockHeight ) ) {
2017-01-29 09:22:14 +01:00
LogPrintf ( " CInstantSend::CheckAndRemove -- Removing expired Transaction Lock Candidate: txid=%s \n " , txHash . ToString ( ) ) ;
std : : map < COutPoint , COutPointLock > : : iterator itOutpointLock = txLockCandidate . mapOutPointLocks . begin ( ) ;
while ( itOutpointLock ! = txLockCandidate . mapOutPointLocks . end ( ) ) {
mapLockedOutpoints . erase ( itOutpointLock - > first ) ;
mapVotedOutpoints . erase ( itOutpointLock - > first ) ;
+ + itOutpointLock ;
2015-02-04 21:20:13 +01:00
}
2017-01-29 09:22:14 +01:00
mapLockRequestAccepted . erase ( txHash ) ;
mapLockRequestRejected . erase ( txHash ) ;
mapTxLockCandidates . erase ( itLockCandidate + + ) ;
} else {
+ + itLockCandidate ;
}
}
2015-02-04 21:20:13 +01:00
2017-01-29 09:22:14 +01:00
// remove expired votes
std : : map < uint256 , CTxLockVote > : : iterator itVote = mapTxLockVotes . begin ( ) ;
while ( itVote ! = mapTxLockVotes . end ( ) ) {
2017-08-25 14:57:05 +02:00
if ( itVote - > second . IsExpired ( nCachedBlockHeight ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::CheckAndRemove -- Removing expired vote: txid=%s masternode=%s \n " ,
itVote - > second . GetTxHash ( ) . ToString ( ) , itVote - > second . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
mapTxLockVotes . erase ( itVote + + ) ;
2014-12-09 02:17:57 +01:00
} else {
2017-01-29 09:22:14 +01:00
+ + itVote ;
2014-12-09 02:17:57 +01:00
}
}
2016-11-22 15:54:26 +01:00
2017-01-29 09:22:14 +01:00
// remove expired orphan votes
std : : map < uint256 , CTxLockVote > : : iterator itOrphanVote = mapTxLockVotesOrphan . begin ( ) ;
while ( itOrphanVote ! = mapTxLockVotesOrphan . end ( ) ) {
2017-09-14 13:41:40 +02:00
if ( itOrphanVote - > second . IsTimedOut ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::CheckAndRemove -- Removing timed out orphan vote: txid=%s masternode=%s \n " ,
2017-01-29 09:22:14 +01:00
itOrphanVote - > second . GetTxHash ( ) . ToString ( ) , itOrphanVote - > second . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
2017-02-04 19:17:45 +01:00
mapTxLockVotes . erase ( itOrphanVote - > first ) ;
2017-01-29 09:22:14 +01:00
mapTxLockVotesOrphan . erase ( itOrphanVote + + ) ;
2016-11-22 15:54:26 +01:00
} else {
2017-01-29 09:22:14 +01:00
+ + itOrphanVote ;
2016-11-22 15:54:26 +01:00
}
}
2017-01-29 09:22:14 +01:00
// remove expired masternode orphan votes (DOS protection)
std : : map < COutPoint , int64_t > : : iterator itMasternodeOrphan = mapMasternodeOrphanVotes . begin ( ) ;
while ( itMasternodeOrphan ! = mapMasternodeOrphanVotes . end ( ) ) {
if ( itMasternodeOrphan - > second < GetTime ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::CheckAndRemove -- Removing expired orphan masternode vote: masternode=%s \n " ,
itMasternodeOrphan - > first . ToStringShort ( ) ) ;
mapMasternodeOrphanVotes . erase ( itMasternodeOrphan + + ) ;
2016-11-22 15:54:26 +01:00
} else {
2017-01-29 09:22:14 +01:00
+ + itMasternodeOrphan ;
2016-11-22 15:54:26 +01:00
}
}
2017-06-06 01:47:23 +02:00
LogPrintf ( " CInstantSend::CheckAndRemove -- %s \n " , ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
bool CInstantSend : : AlreadyHave ( const uint256 & hash )
{
LOCK ( cs_instantsend ) ;
return mapLockRequestAccepted . count ( hash ) | |
mapLockRequestRejected . count ( hash ) | |
mapTxLockVotes . count ( hash ) ;
}
void CInstantSend : : AcceptLockRequest ( const CTxLockRequest & txLockRequest )
2016-08-05 21:49:45 +02:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
mapLockRequestAccepted . insert ( make_pair ( txLockRequest . GetHash ( ) , txLockRequest ) ) ;
2016-03-23 15:49:35 +01:00
}
2017-01-29 09:22:14 +01:00
void CInstantSend : : RejectLockRequest ( const CTxLockRequest & txLockRequest )
{
LOCK ( cs_instantsend ) ;
mapLockRequestRejected . insert ( make_pair ( txLockRequest . GetHash ( ) , txLockRequest ) ) ;
}
bool CInstantSend : : HasTxLockRequest ( const uint256 & txHash )
{
CTxLockRequest txLockRequestTmp ;
return GetTxLockRequest ( txHash , txLockRequestTmp ) ;
}
bool CInstantSend : : GetTxLockRequest ( const uint256 & txHash , CTxLockRequest & txLockRequestRet )
{
LOCK ( cs_instantsend ) ;
std : : map < uint256 , CTxLockCandidate > : : iterator it = mapTxLockCandidates . find ( txHash ) ;
if ( it = = mapTxLockCandidates . end ( ) ) return false ;
txLockRequestRet = it - > second . txLockRequest ;
return true ;
}
bool CInstantSend : : GetTxLockVote ( const uint256 & hash , CTxLockVote & txLockVoteRet )
{
LOCK ( cs_instantsend ) ;
std : : map < uint256 , CTxLockVote > : : iterator it = mapTxLockVotes . find ( hash ) ;
if ( it = = mapTxLockVotes . end ( ) ) return false ;
txLockVoteRet = it - > second ;
return true ;
}
bool CInstantSend : : IsInstantSendReadyToLock ( const uint256 & txHash )
{
if ( ! fEnableInstantSend | | fLargeWorkForkFound | | fLargeWorkInvalidChainFound | |
! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return false ;
LOCK ( cs_instantsend ) ;
// There must be a successfully verified lock request
// and all outputs must be locked (i.e. have enough signatures)
std : : map < uint256 , CTxLockCandidate > : : iterator it = mapTxLockCandidates . find ( txHash ) ;
return it ! = mapTxLockCandidates . end ( ) & & it - > second . IsAllOutPointsReady ( ) ;
}
bool CInstantSend : : IsLockedInstantSendTransaction ( const uint256 & txHash )
{
if ( ! fEnableInstantSend | | fLargeWorkForkFound | | fLargeWorkInvalidChainFound | |
2017-09-14 13:41:40 +02:00
! sporkManager . IsSporkActive ( SPORK_3_INSTANTSEND_BLOCK_FILTERING ) ) return false ;
2017-01-29 09:22:14 +01:00
LOCK ( cs_instantsend ) ;
// there must be a lock candidate
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate = = mapTxLockCandidates . end ( ) ) return false ;
// which should have outpoints
if ( itLockCandidate - > second . mapOutPointLocks . empty ( ) ) return false ;
// and all of these outputs must be included in mapLockedOutpoints with correct hash
std : : map < COutPoint , COutPointLock > : : iterator itOutpointLock = itLockCandidate - > second . mapOutPointLocks . begin ( ) ;
while ( itOutpointLock ! = itLockCandidate - > second . mapOutPointLocks . end ( ) ) {
uint256 hashLocked ;
if ( ! GetLockedOutPointTxHash ( itOutpointLock - > first , hashLocked ) | | hashLocked ! = txHash ) return false ;
+ + itOutpointLock ;
}
return true ;
}
int CInstantSend : : 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 ) ;
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
return itLockCandidate - > second . CountVotes ( ) ;
}
2016-03-23 15:49:35 +01:00
return - 1 ;
}
2017-08-25 14:56:48 +02:00
int CInstantSend : : GetConfirmations ( const uint256 & nTXHash )
{
return IsLockedInstantSendTransaction ( nTXHash ) ? nInstantSendDepth : 0 ;
}
2017-09-14 13:41:40 +02:00
bool CInstantSend : : IsTxLockCandidateTimedOut ( const uint256 & txHash )
2016-03-23 15:49:35 +01:00
{
2017-01-29 09:22:14 +01:00
if ( ! fEnableInstantSend ) return false ;
2016-03-23 15:49:35 +01:00
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
return ! itLockCandidate - > second . IsAllOutPointsReady ( ) & &
2017-09-14 13:41:40 +02:00
itLockCandidate - > second . IsTimedOut ( ) ;
2017-01-29 09:22:14 +01:00
}
2016-03-23 15:49:35 +01:00
return false ;
}
2017-09-15 20:05:13 +02:00
void CInstantSend : : Relay ( const uint256 & txHash , CConnman & connman )
2017-01-29 09:22:14 +01:00
{
LOCK ( cs_instantsend ) ;
std : : map < uint256 , CTxLockCandidate > : : const_iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
2017-09-15 20:05:13 +02:00
itLockCandidate - > second . Relay ( connman ) ;
2017-01-29 09:22:14 +01:00
}
}
void CInstantSend : : UpdatedBlockTip ( const CBlockIndex * pindex )
{
2017-08-25 14:57:05 +02:00
nCachedBlockHeight = pindex - > nHeight ;
2017-01-29 09:22:14 +01:00
}
void CInstantSend : : SyncTransaction ( const CTransaction & tx , const CBlock * pblock )
{
// Update lock candidates and votes if corresponding tx confirmed
// or went from confirmed to 0-confirmed or conflicted.
if ( tx . IsCoinBase ( ) ) return ;
LOCK2 ( cs_main , cs_instantsend ) ;
uint256 txHash = tx . GetHash ( ) ;
// When tx is 0-confirmed or conflicted, pblock is NULL and nHeightNew should be set to -1
2017-04-11 12:54:34 +02:00
CBlockIndex * pblockindex = NULL ;
if ( pblock ) {
uint256 blockHash = pblock - > GetHash ( ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( blockHash ) ;
if ( mi = = mapBlockIndex . end ( ) | | ! mi - > second ) {
// shouldn't happen
LogPrint ( " instantsend " , " CTxLockRequest::SyncTransaction -- Failed to find block %s \n " , blockHash . ToString ( ) ) ;
return ;
}
pblockindex = mi - > second ;
}
2017-01-29 09:22:14 +01:00
int nHeightNew = pblockindex ? pblockindex - > nHeight : - 1 ;
LogPrint ( " instantsend " , " CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d \n " , txHash . ToString ( ) , nHeightNew ) ;
// Check lock candidates
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d lock candidate updated \n " ,
txHash . ToString ( ) , nHeightNew ) ;
itLockCandidate - > second . SetConfirmedHeight ( nHeightNew ) ;
2017-02-04 19:17:45 +01:00
// Loop through outpoint locks
std : : map < COutPoint , COutPointLock > : : iterator itOutpointLock = itLockCandidate - > second . mapOutPointLocks . begin ( ) ;
while ( itOutpointLock ! = itLockCandidate - > second . mapOutPointLocks . end ( ) ) {
// Check corresponding lock votes
std : : vector < CTxLockVote > vVotes = itOutpointLock - > second . GetVotes ( ) ;
std : : vector < CTxLockVote > : : iterator itVote = vVotes . begin ( ) ;
std : : map < uint256 , CTxLockVote > : : iterator it ;
while ( itVote ! = vVotes . end ( ) ) {
uint256 nVoteHash = itVote - > GetHash ( ) ;
LogPrint ( " instantsend " , " CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated \n " ,
txHash . ToString ( ) , nHeightNew , nVoteHash . ToString ( ) ) ;
it = mapTxLockVotes . find ( nVoteHash ) ;
if ( it ! = mapTxLockVotes . end ( ) ) {
it - > second . SetConfirmedHeight ( nHeightNew ) ;
}
+ + itVote ;
}
+ + itOutpointLock ;
}
2017-01-29 09:22:14 +01:00
}
2017-02-04 19:17:45 +01:00
// check orphan votes
std : : map < uint256 , CTxLockVote > : : iterator itOrphanVote = mapTxLockVotesOrphan . begin ( ) ;
while ( itOrphanVote ! = mapTxLockVotesOrphan . end ( ) ) {
if ( itOrphanVote - > second . GetTxHash ( ) = = txHash ) {
LogPrint ( " instantsend " , " CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated \n " ,
txHash . ToString ( ) , nHeightNew , itOrphanVote - > first . ToString ( ) ) ;
mapTxLockVotes [ itOrphanVote - > first ] . SetConfirmedHeight ( nHeightNew ) ;
}
+ + itOrphanVote ;
2017-01-29 09:22:14 +01:00
}
2017-02-04 19:17:45 +01:00
}
2017-01-29 09:22:14 +01:00
2017-06-06 01:47:23 +02:00
std : : string CInstantSend : : ToString ( )
{
LOCK ( cs_instantsend ) ;
return strprintf ( " Lock Candidates: %llu, Votes %llu " , mapTxLockCandidates . size ( ) , mapTxLockVotes . size ( ) ) ;
}
2017-01-29 09:22:14 +01:00
//
// CTxLockRequest
//
2017-09-14 13:41:40 +02:00
bool CTxLockRequest : : IsValid ( ) const
2017-01-29 09:22:14 +01:00
{
if ( vout . size ( ) < 1 ) return false ;
2017-02-02 23:38:33 +01:00
if ( vin . size ( ) > WARN_MANY_INPUTS ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- WARNING: Too many inputs: tx=%s " , ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
}
LOCK ( cs_main ) ;
if ( ! CheckFinalTx ( * this ) ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Transaction is not final: tx=%s " , ToString ( ) ) ;
return false ;
}
2017-03-01 21:30:43 +01:00
CAmount nValueIn = 0 ;
CAmount nValueOut = 0 ;
2017-01-29 09:22:14 +01:00
BOOST_FOREACH ( const CTxOut & txout , vout ) {
// InstantSend 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 " , " CTxLockRequest::IsValid -- Invalid Script %s " , ToString ( ) ) ;
return false ;
}
nValueOut + = txout . nValue ;
}
BOOST_FOREACH ( const CTxIn & txin , vin ) {
CCoins coins ;
2017-03-01 21:30:43 +01:00
2017-08-25 14:56:48 +02:00
if ( ! GetUTXOCoins ( txin . prevout , coins ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Failed to find UTXO %s \n " , txin . prevout . ToStringShort ( ) ) ;
2017-09-14 13:41:40 +02:00
return false ;
2017-01-29 09:22:14 +01:00
}
2017-09-14 13:41:40 +02:00
int nTxAge = chainActive . Height ( ) - coins . nHeight + 1 ;
2017-01-29 09:22:14 +01:00
// 1 less than the "send IX" gui requires, in case of a block propagating the network at the time
int nConfirmationsRequired = INSTANTSEND_CONFIRMATIONS_REQUIRED - 1 ;
if ( nTxAge < nConfirmationsRequired ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- outpoint %s too new: nTxAge=%d, nConfirmationsRequired=%d, txid=%s \n " ,
txin . prevout . ToStringShort ( ) , nTxAge , nConfirmationsRequired , GetHash ( ) . ToString ( ) ) ;
return false ;
}
2017-09-14 13:41:40 +02:00
nValueIn + = coins . vout [ txin . prevout . n ] . nValue ;
2017-01-29 09:22:14 +01:00
}
2017-09-14 13:41:40 +02:00
if ( nValueIn > sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) * COIN ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Transaction value too high: nValueIn=%d, tx=%s " , nValueIn , ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
if ( nValueIn - nValueOut < GetMinFee ( ) ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- did not include enough fees in transaction: fees=%d, tx=%s " , nValueOut - nValueIn , ToString ( ) ) ;
return false ;
}
return true ;
}
CAmount CTxLockRequest : : GetMinFee ( ) const
{
CAmount nMinFee = MIN_FEE ;
return std : : max ( nMinFee , CAmount ( vin . size ( ) * nMinFee ) ) ;
}
int CTxLockRequest : : GetMaxSignatures ( ) const
{
return vin . size ( ) * COutPointLock : : SIGNATURES_TOTAL ;
}
//
// CTxLockVote
//
bool CTxLockVote : : IsValid ( CNode * pnode ) const
2015-02-01 16:53:49 +01:00
{
2017-09-11 16:13:48 +02:00
if ( ! mnodeman . Has ( outpointMasternode ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Unknown masternode %s \n " , outpointMasternode . ToStringShort ( ) ) ;
2017-09-11 16:13:48 +02:00
mnodeman . AskForMN ( pnode , outpointMasternode ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-14 13:41:40 +02:00
CCoins coins ;
if ( ! GetUTXOCoins ( outpoint , coins ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Failed to find UTXO %s \n " , outpoint . ToStringShort ( ) ) ;
2017-09-14 13:41:40 +02:00
return false ;
2017-01-29 09:22:14 +01:00
}
2017-09-14 13:41:40 +02:00
int nLockInputHeight = coins . nHeight + 4 ;
2017-01-29 09:22:14 +01:00
2017-09-14 15:58:29 +02:00
int nRank ;
if ( ! mnodeman . GetMasternodeRank ( outpointMasternode , nRank , nLockInputHeight , MIN_INSTANTSEND_PROTO_VERSION ) ) {
2017-01-29 09:22:14 +01:00
//can be caused by past versions trying to vote with an invalid protocol
2017-06-06 01:47:23 +02:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Can't calculate rank for masternode %s \n " , outpointMasternode . ToStringShort ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-14 15:58:29 +02:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Masternode %s, rank=%d \n " , outpointMasternode . ToStringShort ( ) , nRank ) ;
2017-01-29 09:22:14 +01:00
int nSignaturesTotal = COutPointLock : : SIGNATURES_TOTAL ;
2017-09-14 15:58:29 +02:00
if ( nRank > nSignaturesTotal ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Masternode %s is not in the top %d (%d), vote hash=%s \n " ,
2017-09-14 15:58:29 +02:00
outpointMasternode . ToStringShort ( ) , nSignaturesTotal , nRank , GetHash ( ) . ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
if ( ! CheckSignature ( ) ) {
LogPrintf ( " CTxLockVote::IsValid -- Signature invalid \n " ) ;
return false ;
}
return true ;
2015-02-01 16:53:49 +01:00
}
2017-01-29 09:22:14 +01:00
uint256 CTxLockVote : : GetHash ( ) const
{
CHashWriter ss ( SER_GETHASH , PROTOCOL_VERSION ) ;
ss < < txHash ;
ss < < outpoint ;
ss < < outpointMasternode ;
return ss . GetHash ( ) ;
}
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 ;
2017-01-29 09:22:14 +01:00
std : : string strMessage = txHash . ToString ( ) + outpoint . ToStringShort ( ) ;
2014-12-31 03:54:00 +01:00
2017-09-11 16:13:48 +02:00
masternode_info_t infoMn ;
2014-12-09 02:17:57 +01:00
2017-09-11 16:13:48 +02:00
if ( ! mnodeman . GetMasternodeInfo ( outpointMasternode , infoMn ) ) {
2017-01-29 09:22:14 +01:00
LogPrintf ( " CTxLockVote::CheckSignature -- Unknown Masternode: masternode=%s \n " , outpointMasternode . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : 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 ;
2017-01-29 09:22:14 +01:00
std : : string strMessage = txHash . ToString ( ) + outpoint . ToStringShort ( ) ;
2014-12-09 02:17:57 +01:00
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : 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 ;
}
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : 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 ;
}
2017-09-15 20:05:13 +02:00
void CTxLockVote : : Relay ( CConnman & connman ) const
2017-01-29 09:22:14 +01:00
{
CInv inv ( MSG_TXLOCK_VOTE , GetHash ( ) ) ;
2017-09-15 20:05:13 +02:00
connman . RelayInv ( inv ) ;
2017-01-29 09:22:14 +01:00
}
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
bool CTxLockVote : : IsExpired ( int nHeight ) const
2014-12-09 02:17:57 +01:00
{
2017-01-29 09:22:14 +01:00
// Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into.
return ( nConfirmedHeight ! = - 1 ) & & ( nHeight - nConfirmedHeight > Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ) ;
}
2014-12-09 02:17:57 +01:00
2017-09-14 13:41:40 +02:00
bool CTxLockVote : : IsTimedOut ( ) const
{
return GetTime ( ) - nTimeCreated > INSTANTSEND_TIMEOUT_SECONDS ;
}
2017-01-29 09:22:14 +01:00
//
// COutPointLock
//
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
bool COutPointLock : : AddVote ( const CTxLockVote & vote )
{
if ( mapMasternodeVotes . count ( vote . GetMasternodeOutpoint ( ) ) )
return false ;
mapMasternodeVotes . insert ( std : : make_pair ( vote . GetMasternodeOutpoint ( ) , vote ) ) ;
return true ;
}
2014-12-09 02:17:57 +01:00
2017-02-04 19:17:45 +01:00
std : : vector < CTxLockVote > COutPointLock : : GetVotes ( ) const
{
std : : vector < CTxLockVote > vRet ;
std : : map < COutPoint , CTxLockVote > : : const_iterator itVote = mapMasternodeVotes . begin ( ) ;
while ( itVote ! = mapMasternodeVotes . end ( ) ) {
vRet . push_back ( itVote - > second ) ;
+ + itVote ;
}
return vRet ;
}
bool COutPointLock : : HasMasternodeVoted ( const COutPoint & outpointMasternodeIn ) const
2017-01-29 09:22:14 +01:00
{
return mapMasternodeVotes . count ( outpointMasternodeIn ) ;
}
2014-12-09 02:17:57 +01:00
2017-09-15 20:05:13 +02:00
void COutPointLock : : Relay ( CConnman & connman ) const
2017-01-29 09:22:14 +01:00
{
std : : map < COutPoint , CTxLockVote > : : const_iterator itVote = mapMasternodeVotes . begin ( ) ;
while ( itVote ! = mapMasternodeVotes . end ( ) ) {
2017-09-15 20:05:13 +02:00
itVote - > second . Relay ( connman ) ;
2017-01-29 09:22:14 +01:00
+ + itVote ;
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
}
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
//
// CTxLockCandidate
//
void CTxLockCandidate : : AddOutPointLock ( const COutPoint & outpoint )
{
mapOutPointLocks . insert ( make_pair ( outpoint , COutPointLock ( outpoint ) ) ) ;
}
2017-09-14 13:41:40 +02:00
void CTxLockCandidate : : MarkOutpointAsAttacked ( const COutPoint & outpoint )
{
std : : map < COutPoint , COutPointLock > : : iterator it = mapOutPointLocks . find ( outpoint ) ;
if ( it ! = mapOutPointLocks . end ( ) )
it - > second . MarkAsAttacked ( ) ;
}
2017-01-29 09:22:14 +01:00
bool CTxLockCandidate : : AddVote ( const CTxLockVote & vote )
{
std : : map < COutPoint , COutPointLock > : : iterator it = mapOutPointLocks . find ( vote . GetOutpoint ( ) ) ;
if ( it = = mapOutPointLocks . end ( ) ) return false ;
return it - > second . AddVote ( vote ) ;
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
bool CTxLockCandidate : : IsAllOutPointsReady ( ) const
2014-12-09 02:17:57 +01:00
{
2017-01-29 09:22:14 +01:00
if ( mapOutPointLocks . empty ( ) ) return false ;
std : : map < COutPoint , COutPointLock > : : const_iterator it = mapOutPointLocks . begin ( ) ;
while ( it ! = mapOutPointLocks . end ( ) ) {
if ( ! it - > second . IsReady ( ) ) return false ;
+ + it ;
}
return true ;
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
bool CTxLockCandidate : : HasMasternodeVoted ( const COutPoint & outpointIn , const COutPoint & outpointMasternodeIn )
2014-12-09 02:17:57 +01:00
{
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , COutPointLock > : : iterator it = mapOutPointLocks . find ( outpointIn ) ;
return it ! = mapOutPointLocks . end ( ) & & it - > second . HasMasternodeVoted ( outpointMasternodeIn ) ;
}
2015-02-04 22:09:50 +01:00
2017-01-29 09:22:14 +01:00
int CTxLockCandidate : : CountVotes ( ) const
{
// Note: do NOT use vote count to figure out if tx is locked, use IsAllOutPointsReady() instead
int nCountVotes = 0 ;
std : : map < COutPoint , COutPointLock > : : const_iterator it = mapOutPointLocks . begin ( ) ;
while ( it ! = mapOutPointLocks . end ( ) ) {
nCountVotes + = it - > second . CountVotes ( ) ;
+ + it ;
}
return nCountVotes ;
}
2015-02-04 22:09:50 +01:00
2017-01-29 09:22:14 +01:00
bool CTxLockCandidate : : IsExpired ( int nHeight ) const
{
// Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into.
return ( nConfirmedHeight ! = - 1 ) & & ( nHeight - nConfirmedHeight > Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ) ;
}
2016-08-05 21:49:45 +02:00
2017-09-14 13:41:40 +02:00
bool CTxLockCandidate : : IsTimedOut ( ) const
{
return GetTime ( ) - nTimeCreated > INSTANTSEND_TIMEOUT_SECONDS ;
}
2017-09-15 20:05:13 +02:00
void CTxLockCandidate : : Relay ( CConnman & connman ) const
2017-01-29 09:22:14 +01:00
{
2017-09-15 20:05:13 +02:00
connman . RelayTransaction ( txLockRequest ) ;
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , COutPointLock > : : const_iterator itOutpointLock = mapOutPointLocks . begin ( ) ;
while ( itOutpointLock ! = mapOutPointLocks . end ( ) ) {
2017-09-15 20:05:13 +02:00
itOutpointLock - > second . Relay ( connman ) ;
2017-01-29 09:22:14 +01:00
+ + itOutpointLock ;
}
2014-12-06 20:41:53 +01:00
}