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-01-29 09:22:14 +01:00
# include "main.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
//
void CInstantSend : : ProcessMessage ( 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
{
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-01-18 07:38:13 +01:00
LOCK2 ( cs_main , 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
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
}
}
2017-01-29 09:22:14 +01:00
bool CInstantSend : : ProcessTxLockRequest ( const CTxLockRequest & txLockRequest )
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 ( ) ;
// Check to see if we conflict with existing completed lock,
// fail if so, there can't be 2 completed locks for the same outpoint
BOOST_FOREACH ( const CTxIn & txin , txLockRequest . vin ) {
std : : map < COutPoint , uint256 > : : iterator it = mapLockedOutpoints . find ( txin . prevout ) ;
if ( it ! = mapLockedOutpoints . end ( ) ) {
// Conflicting with complete lock, ignore this one
// (this could be the one we have but we don't want to try to lock it twice anyway)
LogPrintf ( " CInstantSend::ProcessTxLockRequest -- WARNING: Found conflicting completed Transaction Lock, skipping current one, txid=%s, completed lock txid=%s \n " ,
txLockRequest . GetHash ( ) . ToString ( ) , it - > second . ToString ( ) ) ;
2016-12-08 21:03:57 +01:00
return false ;
}
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
}
}
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 ;
Vote ( txLockCandidate ) ;
2016-11-22 15:54:26 +01:00
ProcessOrphanTxLockVotes ( ) ;
// 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-01-29 09:22:14 +01:00
// Normally we should require all outpoints to be unspent, but in case we are reprocessing
// because of a lot of legit orphan votes we should also check already spent outpoints.
uint256 txHash = txLockRequest . GetHash ( ) ;
if ( ! txLockRequest . IsValid ( ! IsEnoughOrphanVotesForTx ( txLockRequest ) ) ) 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-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 ) ) ;
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-01-29 09:22:14 +01:00
void CInstantSend : : Vote ( CTxLockCandidate & txLockCandidate )
2015-02-04 22:59:19 +01:00
{
if ( ! fMasterNode ) return ;
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-01-29 09:22:14 +01:00
int n = mnodeman . GetMasternodeRank ( activeMasternode . vin , nLockInputHeight , MIN_INSTANTSEND_PROTO_VERSION ) ;
2015-02-02 13:01:06 +01:00
2017-01-29 09:22:14 +01:00
if ( n = = - 1 ) {
LogPrint ( " instantsend " , " CInstantSend::Vote -- Unknown Masternode %s \n " , activeMasternode . vin . prevout . 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 ;
if ( n > nSignaturesTotal ) {
LogPrint ( " instantsend " , " CInstantSend::Vote -- Masternode not in the top %d (%d) \n " , nSignaturesTotal , n ) ;
2017-01-29 20:09:21 +01:00
+ + itOutpointLock ;
2017-01-29 09:22:14 +01:00
continue ;
}
LogPrint ( " instantsend " , " CInstantSend::Vote -- In the top %d (%d) \n " , nSignaturesTotal , n ) ;
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 ) ;
if ( it2 - > second . HasMasternodeVoted ( itOutpointLock - > first , activeMasternode . vin . prevout ) ) {
// 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
CTxLockVote vote ( txHash , itOutpointLock - > first , activeMasternode . vin . prevout ) ;
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 ( ) ) ;
}
}
vote . Relay ( ) ;
}
+ + itOutpointLock ;
}
2014-12-09 02:17:57 +01:00
}
//received a consensus vote
2017-01-29 09:22:14 +01:00
bool CInstantSend : : ProcessTxLockVote ( CNode * pfrom , CTxLockVote & vote )
2014-12-09 02:17:57 +01:00
{
2017-01-29 09:22:14 +01:00
LOCK2 ( cs_main , cs_instantsend ) ;
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 ;
}
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 ) ;
if ( it = = mapTxLockCandidates . end ( ) ) {
2016-11-22 15:54:26 +01:00
if ( ! mapTxLockVotesOrphan . count ( vote . GetHash ( ) ) ) {
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 ( ) ) ;
ProcessTxLockRequest ( itLockRequest - > second ) ;
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-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,
// find out if the same mn voted on this outpoint before
std : : map < uint256 , CTxLockCandidate > : : iterator it2 = mapTxLockCandidates . find ( hash ) ;
if ( it2 - > second . HasMasternodeVoted ( vote . GetOutpoint ( ) , vote . GetMasternodeOutpoint ( ) ) ) {
// yes, it did, refuse to accept a vote to include the same outpoint in another tx
// from the same masternode.
// TODO: apply pose ban score to this masternode?
// NOTE: if we decide to apply pose ban score here, this vote must be relayed further
// to let all other nodes know about this node's misbehaviour and let them apply
// pose ban score too.
LogPrintf ( " CInstantSend::ProcessTxLockVote -- masternode sent conflicting votes! %s \n " , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
return false ;
}
}
2016-11-22 15:54:26 +01:00
}
2017-01-29 09:22:14 +01:00
// we have votes by other masternodes only (so far), let's continue and see who will win
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
CTxLockCandidate & txLockCandidate = it - > second ;
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
2017-01-29 09:22:14 +01:00
vote . Relay ( ) ;
2015-02-01 16:53:49 +01:00
2016-11-22 15:54:26 +01:00
return true ;
}
2017-01-29 09:22:14 +01:00
void CInstantSend : : ProcessOrphanTxLockVotes ( )
2016-11-22 15:54:26 +01:00
{
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
}
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 )
{
LOCK2 ( cs_main , cs_instantsend ) ;
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 ( ) ) ;
if ( ResolveConflicts ( txLockCandidate , Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ) ) {
LockTransactionInputs ( txLockCandidate ) ;
UpdateLockedTransaction ( txLockCandidate ) ;
}
}
}
void CInstantSend : : UpdateLockedTransaction ( const CTxLockCandidate & txLockCandidate )
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
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-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 ;
}
bool CInstantSend : : ResolveConflicts ( const CTxLockCandidate & txLockCandidate , int nMaxBlocks )
{
if ( nMaxBlocks < 1 ) return false ;
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
if ( ! txLockCandidate . IsAllOutPointsReady ( ) ) return true ; // not an error
LOCK ( mempool . cs ) ; // protect mempool.mapNextTx, mempool.mapTx
bool fMempoolConflict = false ;
BOOST_FOREACH ( const CTxIn & txin , txLockCandidate . txLockRequest . vin ) {
uint256 hashConflicting ;
if ( GetLockedOutPointTxHash ( txin . prevout , hashConflicting ) & & txHash ! = hashConflicting ) {
// conflicting with complete lock, ignore current one
LogPrintf ( " CInstantSend::ResolveConflicts -- WARNING: Found conflicting completed Transaction Lock, skipping current one, txid=%s, conflicting txid=%s \n " ,
txHash . ToString ( ) , hashConflicting . ToString ( ) ) ;
return false ; // can't/shouldn't do anything
} 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
// conflicting with tx in mempool
fMempoolConflict = true ;
if ( HasTxLockRequest ( hashConflicting ) ) {
// There can be only one completed lock, the other lock request should never complete
LogPrintf ( " CInstantSend::ResolveConflicts -- WARNING: Found conflicting Transaction Lock Request, replacing by completed Transaction Lock, txid=%s, conflicting txid=%s \n " ,
txHash . ToString ( ) , hashConflicting . ToString ( ) ) ;
} else {
// If this lock is completed, we don't really care about normal conflicting txes.
LogPrintf ( " CInstantSend::ResolveConflicts -- WARNING: Found conflicting transaction, replacing by completed Transaction Lock, txid=%s, conflicting txid=%s \n " ,
txHash . ToString ( ) , hashConflicting . ToString ( ) ) ;
}
}
} // FOREACH
if ( fMempoolConflict ) {
std : : list < CTransaction > removed ;
// remove every tx conflicting with current Transaction Lock Request
mempool . removeConflicts ( txLockCandidate . txLockRequest , removed ) ;
// and try to accept it in mempool again
CValidationState state ;
bool fMissingInputs = false ;
if ( ! AcceptToMemoryPool ( mempool , state , txLockCandidate . txLockRequest , true , & fMissingInputs ) ) {
LogPrintf ( " CInstantSend::ResolveConflicts -- ERROR: Failed to accept completed Transaction Lock to mempool, txid=%s \n " , txHash . ToString ( ) ) ;
return false ;
}
LogPrintf ( " CInstantSend::ResolveConflicts -- Accepted completed Transaction Lock, txid=%s \n " , txHash . ToString ( ) ) ;
return true ;
}
// 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 ;
if ( ! pcoinsTip - > GetCoins ( txin . prevout . hash , coins ) | |
( unsigned int ) txin . prevout . n > = coins . vout . size ( ) | |
coins . vout [ txin . prevout . n ] . IsNull ( ) ) {
// Not in UTXO anymore? A conflicting tx was mined while we were waiting for votes.
// Reprocess tip to make sure tx for this lock is included.
LogPrintf ( " CTxLockRequest::ResolveConflicts -- Failed to find UTXO %s - disconnecting tip... \n " , txin . prevout . ToStringShort ( ) ) ;
2017-01-30 07:01:08 +01:00
if ( ! DisconnectBlocks ( 1 ) ) {
return false ;
}
2017-01-29 09:22:14 +01:00
// Recursively check at "new" old height. Conflicting tx should be rejected by AcceptToMemoryPool.
ResolveConflicts ( txLockCandidate , nMaxBlocks - 1 ) ;
LogPrintf ( " CTxLockRequest::ResolveConflicts -- Failed to find UTXO %s - activating best chain... \n " , txin . prevout . ToStringShort ( ) ) ;
// Activate best chain, block which includes conflicting tx should be rejected by ConnectBlock.
CValidationState state ;
if ( ! ActivateBestChain ( state , Params ( ) ) | | ! state . IsValid ( ) ) {
LogPrintf ( " CTxLockRequest::ResolveConflicts -- ActivateBestChain failed, txid=%s \n " , txin . prevout . ToStringShort ( ) ) ;
return false ;
}
LogPrintf ( " CTxLockRequest::ResolveConflicts -- Failed to find UTXO %s - fixed! \n " , txin . prevout . ToStringShort ( ) ) ;
}
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-01-29 09:22:14 +01:00
if ( ! pCurrentBlockIndex ) 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 ( ) ;
if ( txLockCandidate . IsExpired ( pCurrentBlockIndex - > nHeight ) ) {
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 ( ) ) {
if ( itVote - > second . IsExpired ( pCurrentBlockIndex - > nHeight ) ) {
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 ( ) ) {
if ( GetTime ( ) - itOrphanVote - > second . GetTimeCreated ( ) > ORPHAN_VOTE_SECONDS ) {
LogPrint ( " instantsend " , " CInstantSend::CheckAndRemove -- Removing expired orphan vote: txid=%s masternode=%s \n " ,
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
}
}
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 | |
! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return false ;
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-01-29 09:22:14 +01:00
bool CInstantSend : : IsTxLockRequestTimedOut ( 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 ( ) & &
itLockCandidate - > second . txLockRequest . IsTimedOut ( ) ;
}
2016-03-23 15:49:35 +01:00
return false ;
}
2017-01-29 09:22:14 +01:00
void CInstantSend : : Relay ( const uint256 & txHash )
{
LOCK ( cs_instantsend ) ;
std : : map < uint256 , CTxLockCandidate > : : const_iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
itLockCandidate - > second . Relay ( ) ;
}
}
void CInstantSend : : UpdatedBlockTip ( const CBlockIndex * pindex )
{
pCurrentBlockIndex = pindex ;
}
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
//
// CTxLockRequest
//
bool CTxLockRequest : : IsValid ( bool fRequireUnspent ) const
{
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 ;
int nPrevoutHeight = 0 ;
2017-03-01 21:30:43 +01:00
CAmount nValue = 0 ;
2017-01-29 09:22:14 +01:00
if ( ! pcoinsTip - > GetCoins ( txin . prevout . hash , coins ) | |
( unsigned int ) txin . prevout . n > = coins . vout . size ( ) | |
coins . vout [ txin . prevout . n ] . IsNull ( ) ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Failed to find UTXO %s \n " , txin . prevout . ToStringShort ( ) ) ;
// Normally above sould be enough, but in case we are reprocessing this because of
// a lot of legit orphan votes we should also check already spent outpoints.
if ( fRequireUnspent ) return false ;
CTransaction txOutpointCreated ;
uint256 nHashOutpointConfirmed ;
if ( ! GetTransaction ( txin . prevout . hash , txOutpointCreated , Params ( ) . GetConsensus ( ) , nHashOutpointConfirmed , true ) | | nHashOutpointConfirmed = = uint256 ( ) ) {
2017-03-01 21:30:43 +01:00
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Failed to find outpoint %s \n " , txin . prevout . ToStringShort ( ) ) ;
return false ;
}
if ( txin . prevout . n > = txOutpointCreated . vout . size ( ) ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Outpoint %s is out of bounds, size() = %lld \n " ,
txin . prevout . ToStringShort ( ) , txOutpointCreated . vout . size ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
BlockMap : : iterator mi = mapBlockIndex . find ( nHashOutpointConfirmed ) ;
2017-03-01 21:30:43 +01:00
if ( mi = = mapBlockIndex . end ( ) | | ! mi - > second ) {
// shouldn't happen
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Failed to find block %s for outpoint %s \n " ,
nHashOutpointConfirmed . ToString ( ) , txin . prevout . ToStringShort ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-03-01 21:30:43 +01:00
nPrevoutHeight = mi - > second - > nHeight ;
nValue = txOutpointCreated . vout [ txin . prevout . n ] . nValue ;
} else {
nPrevoutHeight = coins . nHeight ;
nValue = coins . vout [ txin . prevout . n ] . nValue ;
2017-01-29 09:22:14 +01:00
}
2017-03-01 21:30:43 +01:00
int nTxAge = chainActive . Height ( ) - nPrevoutHeight + 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-03-01 21:30:43 +01:00
nValueIn + = nValue ;
2017-01-29 09:22:14 +01:00
}
if ( nValueOut > sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) * COIN ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Transaction value too high: nValueOut=%d, tx=%s " , nValueOut , ToString ( ) ) ;
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 ;
}
bool CTxLockRequest : : IsTimedOut ( ) const
{
return GetTime ( ) - nTimeCreated > TIMEOUT_SECONDS ;
}
//
// CTxLockVote
//
bool CTxLockVote : : IsValid ( CNode * pnode ) const
2015-02-01 16:53:49 +01:00
{
2017-01-29 09:22:14 +01:00
if ( ! mnodeman . Has ( CTxIn ( outpointMasternode ) ) ) {
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Unknown masternode %s \n " , outpointMasternode . ToStringShort ( ) ) ;
mnodeman . AskForMN ( pnode , CTxIn ( outpointMasternode ) ) ;
return false ;
}
int nPrevoutHeight = GetUTXOHeight ( outpoint ) ;
if ( nPrevoutHeight = = - 1 ) {
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Failed to find UTXO %s \n " , outpoint . ToStringShort ( ) ) ;
// Validating utxo set is not enough, votes can arrive after outpoint was already spent,
// if lock request was mined. We should process them too to count them later if they are legit.
CTransaction txOutpointCreated ;
uint256 nHashOutpointConfirmed ;
if ( ! GetTransaction ( outpoint . hash , txOutpointCreated , Params ( ) . GetConsensus ( ) , nHashOutpointConfirmed , true ) | | nHashOutpointConfirmed = = uint256 ( ) ) {
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Failed to find outpoint %s \n " , outpoint . ToStringShort ( ) ) ;
return false ;
}
LOCK ( cs_main ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( nHashOutpointConfirmed ) ;
if ( mi = = mapBlockIndex . end ( ) | | ! mi - > second ) {
// not on this chain?
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Failed to find block %s for outpoint %s \n " , nHashOutpointConfirmed . ToString ( ) , outpoint . ToStringShort ( ) ) ;
return false ;
}
nPrevoutHeight = mi - > second - > nHeight ;
}
int nLockInputHeight = nPrevoutHeight + 4 ;
int n = mnodeman . GetMasternodeRank ( CTxIn ( outpointMasternode ) , nLockInputHeight , MIN_INSTANTSEND_PROTO_VERSION ) ;
if ( n = = - 1 ) {
//can be caused by past versions trying to vote with an invalid protocol
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Outdated masternode %s \n " , outpointMasternode . ToStringShort ( ) ) ;
return false ;
}
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Masternode %s, rank=%d \n " , outpointMasternode . ToStringShort ( ) , n ) ;
int nSignaturesTotal = COutPointLock : : SIGNATURES_TOTAL ;
if ( n > nSignaturesTotal ) {
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Masternode %s is not in the top %d (%d), vote hash=%s \n " ,
outpointMasternode . ToStringShort ( ) , nSignaturesTotal , n , GetHash ( ) . ToString ( ) ) ;
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-01-29 09:22:14 +01:00
masternode_info_t infoMn = mnodeman . GetMasternodeInfo ( CTxIn ( outpointMasternode ) ) ;
2014-12-09 02:17:57 +01:00
2016-11-21 21:40:32 +01:00
if ( ! infoMn . fInfoValid ) {
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-01-29 09:22:14 +01:00
void CTxLockVote : : Relay ( ) const
{
CInv inv ( MSG_TXLOCK_VOTE , GetHash ( ) ) ;
RelayInv ( inv ) ;
}
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-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-01-29 09:22:14 +01:00
void COutPointLock : : Relay ( ) const
{
std : : map < COutPoint , CTxLockVote > : : const_iterator itVote = mapMasternodeVotes . begin ( ) ;
while ( itVote ! = mapMasternodeVotes . end ( ) ) {
itVote - > second . Relay ( ) ;
+ + 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 ) ) ) ;
}
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-01-29 09:22:14 +01:00
void CTxLockCandidate : : Relay ( ) const
{
RelayTransaction ( txLockRequest ) ;
std : : map < COutPoint , COutPointLock > : : const_iterator itOutpointLock = mapOutPointLocks . begin ( ) ;
while ( itOutpointLock ! = mapOutPointLocks . end ( ) ) {
itOutpointLock - > second . Relay ( ) ;
+ + itOutpointLock ;
}
2014-12-06 20:41:53 +01:00
}