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
//
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-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
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 ) {
2017-06-06 01:47:23 +02:00
LogPrint ( " instantsend " , " CInstantSend::Vote -- Can't calculate rank for 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-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 ;
}
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-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 ( ) ) {
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 )
{
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 ( ) ) ;
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-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-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 ;
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.
// 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-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 ( ) ) {
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
}
}
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 | |
! 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-08-25 14:56:48 +02:00
int CInstantSend : : GetConfirmations ( const uint256 & nTXHash )
{
return IsLockedInstantSendTransaction ( nTXHash ) ? nInstantSendDepth : 0 ;
}
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 )
{
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
//
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-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 ( ) ) ;
// 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
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 ;
}
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 ( ) ) ;
Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537)
* net: move CBanDB and CAddrDB out of net.h/cpp
This will eventually solve a circular dependency
* net: Create CConnman to encapsulate p2p connections
* net: Move socket binding into CConnman
* net: move OpenNetworkConnection into CConnman
* net: move ban and addrman functions into CConnman
* net: Add oneshot functions to CConnman
* net: move added node functions to CConnman
* net: Add most functions needed for vNodes to CConnman
* net: handle nodesignals in CConnman
* net: Pass CConnection to wallet rather than using the global
* net: Add rpc error for missing/disabled p2p functionality
* net: Pass CConnman around as needed
* gui: add NodeID to the peer table
* net: create generic functor accessors and move vNodes to CConnman
* net: move whitelist functions into CConnman
* net: move nLastNodeId to CConnman
* net: move nLocalHostNonce to CConnman
This behavior seems to have been quite racy and broken.
Move nLocalHostNonce into CNode, and check received nonces against all
non-fully-connected nodes. If there's a match, assume we've connected
to ourself.
* net: move messageHandlerCondition to CConnman
* net: move send/recv statistics to CConnman
* net: move SendBufferSize/ReceiveFloodSize to CConnman
* net: move nLocalServices/nRelevantServices to CConnman
These are in-turn passed to CNode at connection time. This allows us to offer
different services to different peers (or test the effects of doing so).
* net: move semOutbound and semMasternodeOutbound to CConnman
* net: SocketSendData returns written size
* net: move max/max-outbound to CConnman
* net: Pass best block known height into CConnman
CConnman then passes the current best height into CNode at creation time.
This way CConnman/CNode have no dependency on main for height, and the signals
only move in one direction.
This also helps to prevent identity leakage a tiny bit. Before this change, an
attacker could theoretically make 2 connections on different interfaces. They
would connect fully on one, and only establish the initial connection on the
other. Once they receive a new block, they would relay it to your first
connection, and immediately commence the version handshake on the second. Since
the new block height is reflected immediately, they could attempt to learn
whether the two connections were correlated.
This is, of course, incredibly unlikely to work due to the small timings
involved and receipt from other senders. But it doesn't hurt to lock-in
nBestHeight at the time of connection, rather than letting the remote choose
the time.
* net: pass CClientUIInterface into CConnman
* net: Drop StartNode/StopNode and use CConnman directly
* net: Introduce CConnection::Options to avoid passing so many params
* net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options
* net: move vNodesDisconnected into CConnman
* Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting
* Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead
* net: move MAX_FEELER_CONNECTIONS into connman
2017-07-21 11:35:19 +02:00
g_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-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
{
Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537)
* net: move CBanDB and CAddrDB out of net.h/cpp
This will eventually solve a circular dependency
* net: Create CConnman to encapsulate p2p connections
* net: Move socket binding into CConnman
* net: move OpenNetworkConnection into CConnman
* net: move ban and addrman functions into CConnman
* net: Add oneshot functions to CConnman
* net: move added node functions to CConnman
* net: Add most functions needed for vNodes to CConnman
* net: handle nodesignals in CConnman
* net: Pass CConnection to wallet rather than using the global
* net: Add rpc error for missing/disabled p2p functionality
* net: Pass CConnman around as needed
* gui: add NodeID to the peer table
* net: create generic functor accessors and move vNodes to CConnman
* net: move whitelist functions into CConnman
* net: move nLastNodeId to CConnman
* net: move nLocalHostNonce to CConnman
This behavior seems to have been quite racy and broken.
Move nLocalHostNonce into CNode, and check received nonces against all
non-fully-connected nodes. If there's a match, assume we've connected
to ourself.
* net: move messageHandlerCondition to CConnman
* net: move send/recv statistics to CConnman
* net: move SendBufferSize/ReceiveFloodSize to CConnman
* net: move nLocalServices/nRelevantServices to CConnman
These are in-turn passed to CNode at connection time. This allows us to offer
different services to different peers (or test the effects of doing so).
* net: move semOutbound and semMasternodeOutbound to CConnman
* net: SocketSendData returns written size
* net: move max/max-outbound to CConnman
* net: Pass best block known height into CConnman
CConnman then passes the current best height into CNode at creation time.
This way CConnman/CNode have no dependency on main for height, and the signals
only move in one direction.
This also helps to prevent identity leakage a tiny bit. Before this change, an
attacker could theoretically make 2 connections on different interfaces. They
would connect fully on one, and only establish the initial connection on the
other. Once they receive a new block, they would relay it to your first
connection, and immediately commence the version handshake on the second. Since
the new block height is reflected immediately, they could attempt to learn
whether the two connections were correlated.
This is, of course, incredibly unlikely to work due to the small timings
involved and receipt from other senders. But it doesn't hurt to lock-in
nBestHeight at the time of connection, rather than letting the remote choose
the time.
* net: pass CClientUIInterface into CConnman
* net: Drop StartNode/StopNode and use CConnman directly
* net: Introduce CConnection::Options to avoid passing so many params
* net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options
* net: move vNodesDisconnected into CConnman
* Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting
* Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead
* net: move MAX_FEELER_CONNECTIONS into connman
2017-07-21 11:35:19 +02:00
g_connman - > RelayTransaction ( txLockRequest ) ;
2017-01-29 09:22:14 +01:00
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
}