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"
2018-06-11 12:14:24 +02:00
# include "masternode-payments.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"
2018-03-15 10:21:43 +01:00
# include "netmessagemaker.h"
2016-12-20 14:27:59 +01:00
# 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"
2017-12-01 19:53:34 +01:00
# include "validationinterface.h"
2016-12-19 12:38:35 +01:00
# include "warnings.h"
2017-12-01 19:53:34 +01:00
# ifdef ENABLE_WALLET
# include "wallet/wallet.h"
# endif // ENABLE_WALLET
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
2017-12-01 19:53:34 +01:00
# ifdef ENABLE_WALLET
2016-08-05 21:49:45 +02:00
extern CWallet * pwalletMain ;
2017-12-01 19:53:34 +01:00
# endif // ENABLE_WALLET
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 ;
2018-07-12 11:01:48 +02:00
const std : : string CInstantSend : : SERIALIZATION_VERSION_STRING = " CInstantSend-Version-1 " ;
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
//
2018-05-13 22:58:01 +02:00
// step 1) Some node announces intention to lock transaction inputs via "txlockrequest" message (ix)
// step 2) Top COutPointLock::SIGNATURES_TOTAL masternodes per each spent outpoint push "txlockvote" message (txlvote)
// step 3) Once there are COutPointLock::SIGNATURES_REQUIRED valid "txlockvote" messages (txlvote) per each spent outpoint
// for a corresponding "txlockrequest" message (ix), all outpoints from that tx are treated as locked
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
//
// CInstantSend
//
2017-02-06 14:31:37 +01:00
void CInstantSend : : ProcessMessage ( CNode * pfrom , const std : : string & strCommand , CDataStream & vRecv , CConnman & connman )
2014-12-09 02:17:57 +01:00
{
2016-08-05 21:49:45 +02:00
if ( fLiteMode ) return ; // disable all Dash specific functionality
2016-09-01 16:55:25 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2016-08-29 21:11:34 +02:00
2017-02-16 14:01:00 +01:00
// NOTE: NetMsgType::TXLOCKREQUEST is handled via ProcessMessage() in net_processing.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
{
2018-03-15 10:21:43 +01:00
if ( pfrom - > nVersion < MIN_INSTANTSEND_PROTO_VERSION ) {
LogPrint ( " instantsend " , " TXLOCKVOTE -- peer=%d using obsolete version %i \n " , pfrom - > id , pfrom - > nVersion ) ;
connman . PushMessage ( pfrom , CNetMsgMaker ( pfrom - > GetSendVersion ( ) ) . Make ( NetMsgType : : REJECT , strCommand , REJECT_OBSOLETE ,
strprintf ( " Version must be %d or greater " , MIN_INSTANTSEND_PROTO_VERSION ) ) ) ;
return ;
}
2017-01-29 09:22:14 +01:00
2016-11-17 01:31:35 +01:00
CTxLockVote vote ;
2016-08-05 21:49:45 +02:00
vRecv > > vote ;
2014-12-09 02:17:57 +01:00
2017-11-22 15:27:06 +01:00
uint256 nVoteHash = vote . GetHash ( ) ;
pfrom - > setAskFor . erase ( nVoteHash ) ;
// Ignore any InstantSend messages until masternode list is synced
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) return ;
2018-03-09 15:15:48 +01:00
{
LOCK ( cs_instantsend ) ;
auto ret = mapTxLockVotes . emplace ( nVoteHash , vote ) ;
if ( ! ret . second ) return ;
}
2014-12-31 03:54:00 +01:00
2018-02-25 06:33:40 +01:00
ProcessNewTxLockVote ( pfrom , vote , connman ) ;
2015-02-01 21:37:20 +01:00
2015-02-01 16:53:49 +01:00
return ;
2014-12-09 02:17:57 +01:00
}
}
2017-09-15 20:05:13 +02:00
bool CInstantSend : : ProcessTxLockRequest ( const CTxLockRequest & txLockRequest , CConnman & connman )
2016-08-05 21:49:45 +02:00
{
2018-03-09 15:15:48 +01:00
LOCK ( cs_main ) ;
# ifdef ENABLE_WALLET
LOCK ( pwalletMain ? & pwalletMain - > cs_wallet : NULL ) ;
# endif
LOCK2 ( mempool . cs , cs_instantsend ) ;
2015-02-04 11:44:41 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockRequest . GetHash ( ) ;
2017-09-14 13:41:40 +02:00
// Check to see if we conflict with existing completed lock
2018-02-06 12:09:33 +01:00
for ( const auto & txin : txLockRequest . tx - > vin ) {
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , uint256 > : : iterator it = mapLockedOutpoints . find ( txin . prevout ) ;
2017-09-14 13:41:40 +02:00
if ( it ! = mapLockedOutpoints . end ( ) & & it - > second ! = txLockRequest . GetHash ( ) ) {
// Conflicting with complete lock, proceed to see if we should cancel them both
LogPrintf ( " CInstantSend::ProcessTxLockRequest -- WARNING: Found conflicting completed Transaction Lock, txid=%s, completed lock txid=%s \n " ,
2017-01-29 09:22:14 +01:00
txLockRequest . GetHash ( ) . ToString ( ) , it - > second . ToString ( ) ) ;
2016-12-08 21:03:57 +01:00
}
2016-09-05 18:09:25 +02:00
}
2015-02-04 11:44:41 +01:00
2017-01-29 09:22:14 +01:00
// Check to see if there are votes for conflicting request,
// if so - do not fail, just warn user
2018-02-06 12:09:33 +01:00
for ( const auto & txin : txLockRequest . tx - > vin ) {
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , std : : set < uint256 > > : : iterator it = mapVotedOutpoints . find ( txin . prevout ) ;
if ( it ! = mapVotedOutpoints . end ( ) ) {
2018-02-06 12:09:33 +01:00
for ( const auto & hash : it - > second ) {
2017-01-29 09:22:14 +01:00
if ( hash ! = txLockRequest . GetHash ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::ProcessTxLockRequest -- Double spend attempt! %s \n " , txin . prevout . ToStringShort ( ) ) ;
// do not fail here, let it go and see which one will get the votes to be locked
2017-09-14 13:41:40 +02:00
// TODO: notify zmq+script
2017-01-29 09:22:14 +01:00
}
}
2015-02-04 11:44:41 +01:00
}
}
2017-01-29 09:22:14 +01:00
if ( ! CreateTxLockCandidate ( txLockRequest ) ) {
2016-11-22 15:54:26 +01:00
// smth is not right
2017-01-29 09:22:14 +01:00
LogPrintf ( " CInstantSend::ProcessTxLockRequest -- CreateTxLockCandidate failed, txid=%s \n " , txHash . ToString ( ) ) ;
2016-11-22 15:54:26 +01:00
return false ;
}
2017-01-29 09:22:14 +01:00
LogPrintf ( " CInstantSend::ProcessTxLockRequest -- accepted, txid=%s \n " , txHash . ToString ( ) ) ;
2016-11-22 15:54:26 +01:00
// Masternodes will sometimes propagate votes before the transaction is known to the client.
2018-02-25 06:33:40 +01:00
// If this just happened - process orphan votes, lock inputs, resolve conflicting locks,
// update transaction status forcing external script/zmq notifications.
ProcessOrphanTxLockVotes ( ) ;
2018-01-06 11:06:43 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
TryToFinalizeLockCandidate ( itLockCandidate - > second ) ;
2016-11-22 15:54:26 +01:00
return true ;
}
2017-01-29 09:22:14 +01:00
bool CInstantSend : : CreateTxLockCandidate ( const CTxLockRequest & txLockRequest )
2014-12-09 02:17:57 +01:00
{
2017-09-14 13:41:40 +02:00
if ( ! txLockRequest . IsValid ( ) ) return false ;
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
LOCK ( cs_instantsend ) ;
2015-02-04 22:09:50 +01:00
2017-09-14 13:41:40 +02:00
uint256 txHash = txLockRequest . GetHash ( ) ;
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate = = mapTxLockCandidates . end ( ) ) {
LogPrintf ( " CInstantSend::CreateTxLockCandidate -- new, txid=%s \n " , txHash . ToString ( ) ) ;
2016-11-21 21:40:32 +01:00
2017-01-29 09:22:14 +01:00
CTxLockCandidate txLockCandidate ( txLockRequest ) ;
// all inputs should already be checked by txLockRequest.IsValid() above, just use them now
2018-04-23 12:25:04 +02:00
for ( const auto & txin : txLockRequest . tx - > vin ) {
2017-01-29 09:22:14 +01:00
txLockCandidate . AddOutPointLock ( txin . prevout ) ;
}
2016-11-21 21:40:32 +01:00
mapTxLockCandidates . insert ( std : : make_pair ( txHash , txLockCandidate ) ) ;
2017-09-14 13:41:40 +02:00
} else if ( ! itLockCandidate - > second . txLockRequest ) {
// i.e. empty Transaction Lock Candidate was created earlier, let's update it with actual data
itLockCandidate - > second . txLockRequest = txLockRequest ;
if ( itLockCandidate - > second . IsTimedOut ( ) ) {
LogPrintf ( " CInstantSend::CreateTxLockCandidate -- timed out, txid=%s \n " , txHash . ToString ( ) ) ;
return false ;
}
LogPrintf ( " CInstantSend::CreateTxLockCandidate -- update empty, txid=%s \n " , txHash . ToString ( ) ) ;
// all inputs should already be checked by txLockRequest.IsValid() above, just use them now
2018-04-23 12:25:04 +02:00
for ( const auto & txin : txLockRequest . tx - > vin ) {
2017-09-14 13:41:40 +02:00
itLockCandidate - > second . AddOutPointLock ( txin . prevout ) ;
}
2015-02-04 22:09:50 +01:00
} else {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::CreateTxLockCandidate -- seen, txid=%s \n " , txHash . ToString ( ) ) ;
2015-02-04 22:09:50 +01:00
}
2017-01-29 09:22:14 +01:00
return true ;
2015-02-04 22:59:19 +01:00
}
2017-09-14 13:41:40 +02:00
void CInstantSend : : CreateEmptyTxLockCandidate ( const uint256 & txHash )
{
if ( mapTxLockCandidates . find ( txHash ) ! = mapTxLockCandidates . end ( ) )
return ;
LogPrintf ( " CInstantSend::CreateEmptyTxLockCandidate -- new, txid=%s \n " , txHash . ToString ( ) ) ;
const CTxLockRequest txLockRequest = CTxLockRequest ( ) ;
mapTxLockCandidates . insert ( std : : make_pair ( txHash , CTxLockCandidate ( txLockRequest ) ) ) ;
}
2018-01-06 11:06:43 +01:00
void CInstantSend : : Vote ( const uint256 & txHash , CConnman & connman )
{
AssertLockHeld ( cs_main ) ;
2018-03-09 15:15:48 +01:00
# ifdef ENABLE_WALLET
LOCK ( pwalletMain ? & pwalletMain - > cs_wallet : NULL ) ;
# endif
CTxLockRequest dummyRequest ;
CTxLockCandidate txLockCandidate ( dummyRequest ) ;
{
LOCK ( cs_instantsend ) ;
auto itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate = = mapTxLockCandidates . end ( ) ) return ;
txLockCandidate = itLockCandidate - > second ;
Vote ( txLockCandidate , connman ) ;
}
2018-01-06 11:06:43 +01:00
// Let's see if our vote changed smth
2018-03-09 15:15:48 +01:00
LOCK2 ( mempool . cs , cs_instantsend ) ;
TryToFinalizeLockCandidate ( txLockCandidate ) ;
2018-01-06 11:06:43 +01:00
}
2017-09-15 20:05:13 +02:00
void CInstantSend : : Vote ( CTxLockCandidate & txLockCandidate , CConnman & connman )
2015-02-04 22:59:19 +01:00
{
2018-01-26 02:11:01 +01:00
if ( ! fMasternodeMode ) return ;
2017-09-14 13:41:40 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2015-02-04 22:59:19 +01:00
2018-03-09 15:15:48 +01:00
AssertLockHeld ( cs_main ) ;
AssertLockHeld ( cs_instantsend ) ;
2015-02-06 05:41:17 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . GetHash ( ) ;
2018-01-06 11:06:43 +01:00
// We should never vote on a Transaction Lock Request that was not (yet) accepted by the mempool
if ( mapLockRequestAccepted . find ( txHash ) = = mapLockRequestAccepted . end ( ) ) return ;
2017-01-29 09:22:14 +01:00
// check if we need to vote on this candidate's outpoints,
// it's possible that we need to vote for several of them
2018-07-12 11:07:51 +02:00
for ( auto & outpointLockPair : txLockCandidate . mapOutPointLocks ) {
int nPrevoutHeight = GetUTXOHeight ( outpointLockPair . first ) ;
2017-01-29 09:22:14 +01:00
if ( nPrevoutHeight = = - 1 ) {
2018-07-12 11:07:51 +02:00
LogPrint ( " instantsend " , " CInstantSend::Vote -- Failed to find UTXO %s \n " , outpointLockPair . first . ToStringShort ( ) ) ;
2017-01-29 09:22:14 +01:00
return ;
}
2015-02-04 22:59:19 +01:00
2018-03-29 17:08:24 +02:00
int nLockInputHeight = nPrevoutHeight + Params ( ) . GetConsensus ( ) . nInstantSendConfirmationsRequired - 2 ;
2015-02-06 05:41:17 +01:00
2017-09-14 15:58:29 +02:00
int nRank ;
2018-06-11 12:14:24 +02:00
int nMinRequiredProtocol = std : : max ( MIN_INSTANTSEND_PROTO_VERSION , mnpayments . GetMinMasternodePaymentsProto ( ) ) ;
if ( ! mnodeman . GetMasternodeRank ( activeMasternode . outpoint , nRank , nLockInputHeight , nMinRequiredProtocol ) ) {
2017-09-11 16:13:48 +02:00
LogPrint ( " instantsend " , " CInstantSend::Vote -- Can't calculate rank for masternode %s \n " , activeMasternode . outpoint . ToStringShort ( ) ) ;
2017-01-29 09:22:14 +01:00
continue ;
}
2015-02-06 20:07:22 +01:00
2017-01-29 09:22:14 +01:00
int nSignaturesTotal = COutPointLock : : SIGNATURES_TOTAL ;
2017-09-14 15:58:29 +02:00
if ( nRank > nSignaturesTotal ) {
LogPrint ( " instantsend " , " CInstantSend::Vote -- Masternode not in the top %d (%d) \n " , nSignaturesTotal , nRank ) ;
2017-01-29 09:22:14 +01:00
continue ;
}
2017-09-14 15:58:29 +02:00
LogPrint ( " instantsend " , " CInstantSend::Vote -- In the top %d (%d) \n " , nSignaturesTotal , nRank ) ;
2017-01-29 09:22:14 +01:00
2018-07-12 11:07:51 +02:00
std : : map < COutPoint , std : : set < uint256 > > : : iterator itVoted = mapVotedOutpoints . find ( outpointLockPair . first ) ;
2017-01-29 09:22:14 +01:00
// 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 ( ) ) {
2018-02-06 12:09:33 +01:00
for ( const auto & hash : itVoted - > second ) {
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator it2 = mapTxLockCandidates . find ( hash ) ;
2018-07-12 11:07:51 +02:00
if ( it2 - > second . HasMasternodeVoted ( outpointLockPair . first , activeMasternode . outpoint ) ) {
2017-01-29 09:22:14 +01:00
// we already voted for this outpoint to be included either in the same tx or in a competing one,
// skip it anyway
fAlreadyVoted = true ;
LogPrintf ( " CInstantSend::Vote -- WARNING: We already voted for this outpoint, skipping: txHash=%s, outpoint=%s \n " ,
2018-07-12 11:07:51 +02:00
txHash . ToString ( ) , outpointLockPair . first . ToStringShort ( ) ) ;
2017-01-29 09:22:14 +01:00
break ;
}
}
}
2017-01-29 20:09:21 +01:00
if ( fAlreadyVoted ) {
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
2018-07-12 11:07:51 +02:00
CTxLockVote vote ( txHash , outpointLockPair . first , activeMasternode . outpoint ) ;
2017-01-29 09:22:14 +01:00
if ( ! vote . Sign ( ) ) {
LogPrintf ( " CInstantSend::Vote -- Failed to sign consensus vote \n " ) ;
return ;
}
if ( ! vote . CheckSignature ( ) ) {
LogPrintf ( " CInstantSend::Vote -- Signature invalid \n " ) ;
return ;
}
// vote constructed sucessfully, let's store and relay it
uint256 nVoteHash = vote . GetHash ( ) ;
mapTxLockVotes . insert ( std : : make_pair ( nVoteHash , vote ) ) ;
2018-07-12 11:07:51 +02:00
if ( outpointLockPair . second . AddVote ( vote ) ) {
2017-01-29 09:22:14 +01:00
LogPrintf ( " CInstantSend::Vote -- Vote created successfully, relaying: txHash=%s, outpoint=%s, vote=%s \n " ,
2018-07-12 11:07:51 +02:00
txHash . ToString ( ) , outpointLockPair . first . ToStringShort ( ) , nVoteHash . ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
if ( itVoted = = mapVotedOutpoints . end ( ) ) {
std : : set < uint256 > setHashes ;
setHashes . insert ( txHash ) ;
2018-07-12 11:07:51 +02:00
mapVotedOutpoints . insert ( std : : make_pair ( outpointLockPair . first , setHashes ) ) ;
2017-01-29 09:22:14 +01:00
} else {
2018-07-12 11:07:51 +02:00
mapVotedOutpoints [ outpointLockPair . first ] . insert ( txHash ) ;
if ( mapVotedOutpoints [ outpointLockPair . first ] . size ( ) > 1 ) {
2017-01-29 09:22:14 +01:00
// 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 " ,
2018-07-12 11:07:51 +02:00
txHash . ToString ( ) , outpointLockPair . first . ToStringShort ( ) , nVoteHash . ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
}
}
2017-09-15 20:05:13 +02:00
vote . Relay ( connman ) ;
2017-01-29 09:22:14 +01:00
}
}
2014-12-09 02:17:57 +01:00
}
2018-02-25 06:33:40 +01:00
bool CInstantSend : : ProcessNewTxLockVote ( CNode * pfrom , const CTxLockVote & vote , CConnman & connman )
2014-12-09 02:17:57 +01:00
{
2017-01-29 09:22:14 +01:00
uint256 txHash = vote . GetTxHash ( ) ;
2018-02-25 06:33:40 +01:00
uint256 nVoteHash = vote . GetHash ( ) ;
2017-01-29 09:22:14 +01:00
2017-09-19 16:51:38 +02:00
if ( ! vote . IsValid ( pfrom , connman ) ) {
2017-01-29 09:22:14 +01:00
// could be because of missing MN
2018-02-25 06:33:40 +01:00
LogPrint ( " instantsend " , " CInstantSend::%s -- Vote is invalid, txid=%s \n " , __func__ , txHash . ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-14 13:41:40 +02:00
// relay valid vote asap
2017-09-15 20:05:13 +02:00
vote . Relay ( connman ) ;
2017-09-14 13:41:40 +02:00
2018-03-09 15:15:48 +01:00
LOCK ( cs_main ) ;
# ifdef ENABLE_WALLET
LOCK ( pwalletMain ? & pwalletMain - > cs_wallet : NULL ) ;
# endif
LOCK2 ( mempool . cs , cs_instantsend ) ;
2016-11-22 15:54:26 +01:00
// Masternodes will sometimes propagate votes before the transaction is known to the client,
// will actually process only after the lock request itself has arrived
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator it = mapTxLockCandidates . find ( txHash ) ;
2017-09-14 13:41:40 +02:00
if ( it = = mapTxLockCandidates . end ( ) | | ! it - > second . txLockRequest ) {
2018-02-25 06:33:40 +01:00
// no or empty tx lock candidate
if ( it = = mapTxLockCandidates . end ( ) ) {
2017-09-14 13:41:40 +02:00
// start timeout countdown after the very first vote
CreateEmptyTxLockCandidate ( txHash ) ;
2016-11-22 15:54:26 +01:00
}
2018-02-25 06:33:40 +01:00
bool fInserted = mapTxLockVotesOrphan . emplace ( nVoteHash , vote ) . second ;
LogPrint ( " instantsend " , " CInstantSend::%s -- Orphan vote: txid=%s masternode=%s %s \n " ,
__func__ , txHash . ToString ( ) , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) , fInserted ? " new " : " seen " ) ;
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
2018-02-25 06:33:40 +01:00
auto itMnOV = mapMasternodeOrphanVotes . find ( vote . GetMasternodeOutpoint ( ) ) ;
if ( itMnOV = = mapMasternodeOrphanVotes . end ( ) ) {
mapMasternodeOrphanVotes . emplace ( vote . GetMasternodeOutpoint ( ) , nMasternodeOrphanExpireTime ) ;
2016-11-22 15:54:26 +01:00
} else {
2018-02-25 06:33:40 +01:00
if ( itMnOV - > second > GetTime ( ) & & itMnOV - > second > GetAverageMasternodeOrphanVoteTime ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::%s -- masternode is spamming orphan Transaction Lock Votes: txid=%s masternode=%s \n " ,
__func__ , txHash . ToString ( ) , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
2016-11-22 15:54:26 +01:00
// Misbehaving(pfrom->id, 1);
return false ;
}
// not spamming, refresh
2018-02-25 06:33:40 +01:00
itMnOV - > second = nMasternodeOrphanExpireTime ;
2016-11-22 15:54:26 +01:00
}
return true ;
}
2014-12-09 02:17:57 +01:00
2018-02-25 06:33:40 +01:00
// We have a valid (non-empty) tx lock candidate
CTxLockCandidate & txLockCandidate = it - > second ;
if ( txLockCandidate . IsTimedOut ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::%s -- too late, Transaction Lock timed out, txid=%s \n " , __func__ , txHash . ToString ( ) ) ;
return false ;
}
LogPrint ( " instantsend " , " CInstantSend::%s -- Transaction Lock Vote, txid=%s \n " , __func__ , txHash . ToString ( ) ) ;
UpdateVotedOutpoints ( vote , txLockCandidate ) ;
if ( ! txLockCandidate . AddVote ( vote ) ) {
// this should never happen
return false ;
}
int nSignatures = txLockCandidate . CountVotes ( ) ;
int nSignaturesMax = txLockCandidate . txLockRequest . GetMaxSignatures ( ) ;
LogPrint ( " instantsend " , " CInstantSend::%s -- Transaction Lock signatures count: %d/%d, vote hash=%s \n " , __func__ ,
nSignatures , nSignaturesMax , nVoteHash . ToString ( ) ) ;
TryToFinalizeLockCandidate ( txLockCandidate ) ;
return true ;
}
bool CInstantSend : : ProcessOrphanTxLockVote ( const CTxLockVote & vote )
{
// 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 ) ;
uint256 txHash = vote . GetTxHash ( ) ;
// We shouldn't process orphan votes without a valid tx lock candidate
std : : map < uint256 , CTxLockCandidate > : : iterator it = mapTxLockCandidates . find ( txHash ) ;
if ( it = = mapTxLockCandidates . end ( ) | | ! it - > second . txLockRequest )
return false ; // this shouldn never happen
2017-09-14 13:41:40 +02:00
CTxLockCandidate & txLockCandidate = it - > second ;
if ( txLockCandidate . IsTimedOut ( ) ) {
2018-02-25 06:33:40 +01:00
LogPrint ( " instantsend " , " CInstantSend::%s -- too late, Transaction Lock timed out, txid=%s \n " , __func__ , txHash . ToString ( ) ) ;
2017-09-14 13:41:40 +02:00
return false ;
}
2018-02-25 06:33:40 +01:00
LogPrint ( " instantsend " , " CInstantSend::%s -- Transaction Lock Vote, txid=%s \n " , __func__ , txHash . ToString ( ) ) ;
UpdateVotedOutpoints ( vote , txLockCandidate ) ;
if ( ! txLockCandidate . AddVote ( vote ) ) {
// this should never happen
return false ;
}
int nSignatures = txLockCandidate . CountVotes ( ) ;
int nSignaturesMax = txLockCandidate . txLockRequest . GetMaxSignatures ( ) ;
LogPrint ( " instantsend " , " CInstantSend::%s -- Transaction Lock signatures count: %d/%d, vote hash=%s \n " ,
__func__ , nSignatures , nSignaturesMax , vote . GetHash ( ) . ToString ( ) ) ;
return true ;
}
void CInstantSend : : UpdateVotedOutpoints ( const CTxLockVote & vote , CTxLockCandidate & txLockCandidate )
{
AssertLockHeld ( cs_instantsend ) ;
uint256 txHash = vote . GetTxHash ( ) ;
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , std : : set < uint256 > > : : iterator it1 = mapVotedOutpoints . find ( vote . GetOutpoint ( ) ) ;
if ( it1 ! = mapVotedOutpoints . end ( ) ) {
2018-02-06 12:09:33 +01:00
for ( const auto & hash : it1 - > second ) {
2017-01-29 09:22:14 +01:00
if ( hash ! = txHash ) {
// same outpoint was already voted to be locked by another tx lock request,
2017-09-14 13:41:40 +02:00
// let's see if it was the same masternode who voted on this outpoint
// for another tx lock request
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator it2 = mapTxLockCandidates . find ( hash ) ;
2017-09-14 13:41:40 +02:00
if ( it2 ! = mapTxLockCandidates . end ( ) & & it2 - > second . HasMasternodeVoted ( vote . GetOutpoint ( ) , vote . GetMasternodeOutpoint ( ) ) ) {
// yes, it was the same masternode
2018-02-25 06:33:40 +01:00
LogPrintf ( " CInstantSend::%s -- masternode sent conflicting votes! %s \n " , __func__ , vote . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
2017-09-14 13:41:40 +02:00
// mark both Lock Candidates as attacked, none of them should complete,
// or at least the new (current) one shouldn't even
// if the second one was already completed earlier
txLockCandidate . MarkOutpointAsAttacked ( vote . GetOutpoint ( ) ) ;
it2 - > second . MarkOutpointAsAttacked ( vote . GetOutpoint ( ) ) ;
// apply maximum PoSe ban score to this masternode i.e. PoSe-ban it instantly
mnodeman . PoSeBan ( vote . GetMasternodeOutpoint ( ) ) ;
// NOTE: This vote must be relayed further to let all other nodes know about such
// misbehaviour of this masternode. This way they should also be able to construct
// conflicting lock and PoSe-ban this masternode.
2017-01-29 09:22:14 +01:00
}
}
2016-11-22 15:54:26 +01:00
}
2017-09-14 13:41:40 +02:00
// store all votes, regardless of them being sent by malicious masternode or not
2017-01-29 09:22:14 +01:00
it1 - > second . insert ( txHash ) ;
} else {
2018-02-25 06:33:40 +01:00
mapVotedOutpoints . emplace ( vote . GetOutpoint ( ) , std : : set < uint256 > ( { txHash } ) ) ;
2014-12-09 02:17:57 +01:00
}
2016-11-22 15:54:26 +01:00
}
2018-02-25 06:33:40 +01:00
void CInstantSend : : ProcessOrphanTxLockVotes ( )
2016-11-22 15:54:26 +01:00
{
2018-03-09 15:15:48 +01:00
AssertLockHeld ( cs_main ) ;
AssertLockHeld ( cs_instantsend ) ;
2017-08-11 20:51:44 +02:00
2016-11-22 15:54:26 +01:00
std : : map < uint256 , CTxLockVote > : : iterator it = mapTxLockVotesOrphan . begin ( ) ;
while ( it ! = mapTxLockVotesOrphan . end ( ) ) {
2018-02-25 06:33:40 +01:00
if ( ProcessOrphanTxLockVote ( it - > second ) ) {
2016-11-22 15:54:26 +01:00
mapTxLockVotesOrphan . erase ( it + + ) ;
} else {
+ + it ;
}
}
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
void CInstantSend : : TryToFinalizeLockCandidate ( const CTxLockCandidate & txLockCandidate )
{
2017-09-14 13:41:40 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2018-03-09 15:15:48 +01:00
AssertLockHeld ( cs_main ) ;
AssertLockHeld ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
2017-09-20 14:02:53 +02:00
uint256 txHash = txLockCandidate . txLockRequest . tx - > GetHash ( ) ;
2017-01-29 09:22:14 +01:00
if ( txLockCandidate . IsAllOutPointsReady ( ) & & ! IsLockedInstantSendTransaction ( txHash ) ) {
// we have enough votes now
LogPrint ( " instantsend " , " CInstantSend::TryToFinalizeLockCandidate -- Transaction Lock is ready to complete, txid=%s \n " , txHash . ToString ( ) ) ;
2017-09-14 13:41:40 +02:00
if ( ResolveConflicts ( txLockCandidate ) ) {
2017-01-29 09:22:14 +01:00
LockTransactionInputs ( txLockCandidate ) ;
UpdateLockedTransaction ( txLockCandidate ) ;
}
}
}
void CInstantSend : : UpdateLockedTransaction ( const CTxLockCandidate & txLockCandidate )
2016-08-05 21:49:45 +02:00
{
2018-03-09 15:15:48 +01:00
// cs_main, cs_wallet and cs_instantsend should be already locked
AssertLockHeld ( cs_main ) ;
2017-08-11 20:51:44 +02:00
# 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-09-20 14:02:53 +02:00
GetMainSignals ( ) . NotifyTransactionLock ( * txLockCandidate . txLockRequest . tx ) ;
2016-03-24 16:54:29 +01:00
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::UpdateLockedTransaction -- done, txid=%s \n " , txHash . ToString ( ) ) ;
2016-03-24 16:54:29 +01:00
}
2017-01-29 09:22:14 +01:00
void CInstantSend : : LockTransactionInputs ( const CTxLockCandidate & txLockCandidate )
2015-02-05 17:48:57 +01:00
{
2017-09-14 13:41:40 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return ;
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2015-02-05 17:48:57 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . GetHash ( ) ;
2016-08-05 21:49:45 +02:00
2017-01-29 09:22:14 +01:00
if ( ! txLockCandidate . IsAllOutPointsReady ( ) ) return ;
2016-08-05 21:49:45 +02:00
2018-07-12 11:07:51 +02:00
for ( const auto & pair : txLockCandidate . mapOutPointLocks ) {
mapLockedOutpoints . insert ( std : : make_pair ( pair . first , txHash ) ) ;
2015-02-05 17:48:57 +01:00
}
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::LockTransactionInputs -- done, txid=%s \n " , txHash . ToString ( ) ) ;
2015-02-05 17:48:57 +01:00
}
2015-02-02 18:33:52 +01:00
2017-01-29 09:22:14 +01:00
bool CInstantSend : : GetLockedOutPointTxHash ( const COutPoint & outpoint , uint256 & hashRet )
2016-08-05 21:49:45 +02:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , uint256 > : : iterator it = mapLockedOutpoints . find ( outpoint ) ;
if ( it = = mapLockedOutpoints . end ( ) ) return false ;
hashRet = it - > second ;
return true ;
}
2017-09-14 13:41:40 +02:00
bool CInstantSend : : ResolveConflicts ( const CTxLockCandidate & txLockCandidate )
2017-01-29 09:22:14 +01:00
{
2018-03-09 15:15:48 +01:00
AssertLockHeld ( cs_main ) ;
AssertLockHeld ( cs_instantsend ) ;
2016-03-24 16:54:29 +01:00
2017-01-29 09:22:14 +01:00
uint256 txHash = txLockCandidate . GetHash ( ) ;
// make sure the lock is ready
2017-09-14 13:41:40 +02:00
if ( ! txLockCandidate . IsAllOutPointsReady ( ) ) return false ;
2017-01-29 09:22:14 +01:00
2018-03-09 15:15:48 +01:00
AssertLockHeld ( mempool . cs ) ; // protect mempool.mapNextTx
2017-01-29 09:22:14 +01:00
2018-02-06 12:09:33 +01:00
for ( const auto & txin : txLockCandidate . txLockRequest . tx - > vin ) {
2017-01-29 09:22:14 +01:00
uint256 hashConflicting ;
if ( GetLockedOutPointTxHash ( txin . prevout , hashConflicting ) & & txHash ! = hashConflicting ) {
2017-09-14 13:41:40 +02:00
// completed lock which conflicts with another completed one?
// this means that majority of MNs in the quorum for this specific tx input are malicious!
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidateConflicting = mapTxLockCandidates . find ( hashConflicting ) ;
if ( itLockCandidate = = mapTxLockCandidates . end ( ) | | itLockCandidateConflicting = = mapTxLockCandidates . end ( ) ) {
// safety check, should never really happen
LogPrintf ( " CInstantSend::ResolveConflicts -- ERROR: Found conflicting completed Transaction Lock, but one of txLockCandidate-s is missing, txid=%s, conflicting txid=%s \n " ,
txHash . ToString ( ) , hashConflicting . ToString ( ) ) ;
return false ;
}
LogPrintf ( " CInstantSend::ResolveConflicts -- WARNING: Found conflicting completed Transaction Lock, dropping both, txid=%s, conflicting txid=%s \n " ,
2017-01-29 09:22:14 +01:00
txHash . ToString ( ) , hashConflicting . ToString ( ) ) ;
2017-09-14 13:41:40 +02:00
CTxLockRequest txLockRequest = itLockCandidate - > second . txLockRequest ;
CTxLockRequest txLockRequestConflicting = itLockCandidateConflicting - > second . txLockRequest ;
itLockCandidate - > second . SetConfirmedHeight ( 0 ) ; // expired
itLockCandidateConflicting - > second . SetConfirmedHeight ( 0 ) ; // expired
CheckAndRemove ( ) ; // clean up
// AlreadyHave should still return "true" for both of them
2018-02-21 20:26:53 +01:00
mapLockRequestRejected . insert ( std : : make_pair ( txHash , txLockRequest ) ) ;
mapLockRequestRejected . insert ( std : : make_pair ( hashConflicting , txLockRequestConflicting ) ) ;
2017-09-14 13:41:40 +02:00
// TODO: clean up mapLockRequestRejected later somehow
// (not a big issue since we already PoSe ban malicious masternodes
// and they won't be able to spam)
// TODO: ban all malicious masternodes permanently, do not accept anything from them, ever
// TODO: notify zmq+script about this double-spend attempt
// and let merchant cancel/hold the order if it's not too late...
// can't do anything else, fallback to regular txes
return false ;
2017-01-29 09:22:14 +01:00
} else if ( mempool . mapNextTx . count ( txin . prevout ) ) {
// check if it's in mempool
2016-06-08 14:01:05 +02:00
hashConflicting = mempool . mapNextTx . find ( txin . prevout ) - > second - > GetHash ( ) ;
2017-01-29 09:22:14 +01:00
if ( txHash = = hashConflicting ) continue ; // matches current, not a conflict, skip to next txin
2017-09-14 13:41:40 +02:00
// conflicts with tx in mempool
LogPrintf ( " CInstantSend::ResolveConflicts -- ERROR: Failed to complete Transaction Lock, conflicts with mempool, txid=%s \n " , txHash . ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-14 13:41:40 +02:00
} // FOREACH
2017-01-29 09:22:14 +01:00
// No conflicts were found so far, check to see if it was already included in block
2016-12-05 08:01:20 +01:00
CTransactionRef txTmp ;
2017-01-29 09:22:14 +01:00
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
2018-02-06 12:09:33 +01:00
for ( const auto & txin : txLockCandidate . txLockRequest . tx - > vin ) {
2017-09-26 16:33:46 +02:00
Coin coin ;
if ( ! GetUTXOCoin ( txin . prevout , coin ) ) {
2017-01-29 09:22:14 +01:00
// Not in UTXO anymore? A conflicting tx was mined while we were waiting for votes.
2017-09-14 13:41:40 +02:00
LogPrintf ( " CInstantSend::ResolveConflicts -- ERROR: Failed to find UTXO %s, can't complete Transaction Lock \n " , txin . prevout . ToStringShort ( ) ) ;
return false ;
2017-01-29 09:22:14 +01:00
}
2016-03-24 16:54:29 +01:00
}
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CInstantSend::ResolveConflicts -- Done, txid=%s \n " , txHash . ToString ( ) ) ;
return true ;
2016-03-24 16:54:29 +01:00
}
2017-01-29 09:22:14 +01:00
int64_t CInstantSend : : GetAverageMasternodeOrphanVoteTime ( )
2015-02-02 18:33:52 +01:00
{
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2016-11-22 15:54:26 +01:00
// NOTE: should never actually call this function when mapMasternodeOrphanVotes is empty
if ( mapMasternodeOrphanVotes . empty ( ) ) return 0 ;
2016-11-21 21:40:32 +01:00
2015-02-02 18:33:52 +01:00
int64_t total = 0 ;
2018-07-12 11:07:51 +02:00
for ( const auto & pair : mapMasternodeOrphanVotes ) {
total + = pair . second ;
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 ( ) ) ;
2018-07-12 11:07:51 +02:00
for ( const auto & pair : txLockCandidate . mapOutPointLocks ) {
mapLockedOutpoints . erase ( pair . first ) ;
mapVotedOutpoints . erase ( pair . first ) ;
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-10-31 15:50:03 +01:00
// remove timed out orphan votes
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockVote > : : iterator itOrphanVote = mapTxLockVotesOrphan . begin ( ) ;
while ( itOrphanVote ! = mapTxLockVotesOrphan . end ( ) ) {
2017-09-14 13:41:40 +02:00
if ( itOrphanVote - > second . IsTimedOut ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::CheckAndRemove -- Removing timed out orphan vote: txid=%s masternode=%s \n " ,
2017-01-29 09:22:14 +01:00
itOrphanVote - > second . GetTxHash ( ) . ToString ( ) , itOrphanVote - > second . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
2017-02-04 19:17:45 +01:00
mapTxLockVotes . erase ( itOrphanVote - > first ) ;
2017-01-29 09:22:14 +01:00
mapTxLockVotesOrphan . erase ( itOrphanVote + + ) ;
2016-11-22 15:54:26 +01:00
} else {
2017-01-29 09:22:14 +01:00
+ + itOrphanVote ;
2016-11-22 15:54:26 +01:00
}
}
2017-10-31 15:50:03 +01:00
// remove invalid votes and votes for failed lock attempts
itVote = mapTxLockVotes . begin ( ) ;
while ( itVote ! = mapTxLockVotes . end ( ) ) {
if ( itVote - > second . IsFailed ( ) ) {
LogPrint ( " instantsend " , " CInstantSend::CheckAndRemove -- Removing vote for failed lock attempt: txid=%s masternode=%s \n " ,
itVote - > second . GetTxHash ( ) . ToString ( ) , itVote - > second . GetMasternodeOutpoint ( ) . ToStringShort ( ) ) ;
mapTxLockVotes . erase ( itVote + + ) ;
} else {
+ + itVote ;
}
}
// remove timed out masternode orphan votes (DOS protection)
2017-01-29 09:22:14 +01:00
std : : map < COutPoint , int64_t > : : iterator itMasternodeOrphan = mapMasternodeOrphanVotes . begin ( ) ;
while ( itMasternodeOrphan ! = mapMasternodeOrphanVotes . end ( ) ) {
if ( itMasternodeOrphan - > second < GetTime ( ) ) {
2017-10-31 15:50:03 +01:00
LogPrint ( " instantsend " , " CInstantSend::CheckAndRemove -- Removing timed out orphan masternode vote: masternode=%s \n " ,
2017-01-29 09:22:14 +01:00
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 ) ;
2018-02-21 20:26:53 +01:00
mapLockRequestAccepted . insert ( std : : 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 ) ;
2018-02-21 20:26:53 +01:00
mapLockRequestRejected . insert ( std : : make_pair ( txLockRequest . GetHash ( ) , txLockRequest ) ) ;
2017-01-29 09:22:14 +01:00
}
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 ) ;
2018-03-21 12:09:28 +01:00
if ( it = = mapTxLockCandidates . end ( ) | | ! it - > second . txLockRequest ) return false ;
2017-01-29 09:22:14 +01:00
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 )
{
2016-12-19 12:38:35 +01:00
if ( ! fEnableInstantSend | | GetfLargeWorkForkFound ( ) | | GetfLargeWorkInvalidChainFound ( ) | |
2017-01-29 09:22:14 +01:00
! 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 ( ) ;
}
2018-07-12 11:01:48 +02:00
void CInstantSend : : Clear ( )
{
LOCK ( cs_instantsend ) ;
mapLockRequestAccepted . clear ( ) ;
mapLockRequestRejected . clear ( ) ;
mapTxLockVotes . clear ( ) ;
mapTxLockVotesOrphan . clear ( ) ;
mapTxLockCandidates . clear ( ) ;
mapVotedOutpoints . clear ( ) ;
mapLockedOutpoints . clear ( ) ;
mapMasternodeOrphanVotes . clear ( ) ;
nCachedBlockHeight = 0 ;
}
2017-01-29 09:22:14 +01:00
bool CInstantSend : : IsLockedInstantSendTransaction ( const uint256 & txHash )
{
2016-12-19 12:38:35 +01:00
if ( ! fEnableInstantSend | | GetfLargeWorkForkFound ( ) | | GetfLargeWorkInvalidChainFound ( ) | |
2017-09-14 13:41:40 +02:00
! sporkManager . IsSporkActive ( SPORK_3_INSTANTSEND_BLOCK_FILTERING ) ) return false ;
2017-01-29 09:22:14 +01:00
LOCK ( cs_instantsend ) ;
// there must be a lock candidate
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate = = mapTxLockCandidates . end ( ) ) return false ;
// which should have outpoints
if ( itLockCandidate - > second . mapOutPointLocks . empty ( ) ) return false ;
// and all of these outputs must be included in mapLockedOutpoints with correct hash
2018-07-12 11:07:51 +02:00
for ( const auto & pair : itLockCandidate - > second . mapOutPointLocks ) {
2017-01-29 09:22:14 +01:00
uint256 hashLocked ;
2018-07-12 11:07:51 +02:00
if ( ! GetLockedOutPointTxHash ( pair . first , hashLocked ) | | hashLocked ! = txHash ) return false ;
2017-01-29 09:22:14 +01:00
}
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-12-19 12:38:35 +01:00
if ( GetfLargeWorkForkFound ( ) | | GetfLargeWorkInvalidChainFound ( ) ) return - 2 ;
2016-09-01 16:55:25 +02:00
if ( ! sporkManager . IsSporkActive ( SPORK_2_INSTANTSEND_ENABLED ) ) return - 3 ;
2016-03-23 15:49:35 +01:00
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
return itLockCandidate - > second . CountVotes ( ) ;
}
2016-03-23 15:49:35 +01:00
return - 1 ;
}
2017-08-25 14:56:48 +02:00
int CInstantSend : : GetConfirmations ( const uint256 & nTXHash )
{
return IsLockedInstantSendTransaction ( nTXHash ) ? nInstantSendDepth : 0 ;
}
2017-09-14 13:41:40 +02:00
bool CInstantSend : : IsTxLockCandidateTimedOut ( const uint256 & txHash )
2016-03-23 15:49:35 +01:00
{
2017-01-29 09:22:14 +01:00
if ( ! fEnableInstantSend ) return false ;
2016-03-23 15:49:35 +01:00
2017-01-18 07:38:13 +01:00
LOCK ( cs_instantsend ) ;
2017-01-29 09:22:14 +01:00
std : : map < uint256 , CTxLockCandidate > : : iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
return ! itLockCandidate - > second . IsAllOutPointsReady ( ) & &
2017-09-14 13:41:40 +02:00
itLockCandidate - > second . IsTimedOut ( ) ;
2017-01-29 09:22:14 +01:00
}
2016-03-23 15:49:35 +01:00
return false ;
}
2017-09-15 20:05:13 +02:00
void CInstantSend : : Relay ( const uint256 & txHash , CConnman & connman )
2017-01-29 09:22:14 +01:00
{
LOCK ( cs_instantsend ) ;
std : : map < uint256 , CTxLockCandidate > : : const_iterator itLockCandidate = mapTxLockCandidates . find ( txHash ) ;
if ( itLockCandidate ! = mapTxLockCandidates . end ( ) ) {
2017-09-15 20:05:13 +02:00
itLockCandidate - > second . Relay ( connman ) ;
2017-01-29 09:22:14 +01:00
}
}
void CInstantSend : : UpdatedBlockTip ( const CBlockIndex * pindex )
{
2017-08-25 14:57:05 +02:00
nCachedBlockHeight = pindex - > nHeight ;
2017-01-29 09:22:14 +01:00
}
2018-01-08 18:41:06 +01:00
void CInstantSend : : SyncTransaction ( const CTransaction & tx , const CBlockIndex * pindex , int posInBlock )
2017-01-29 09:22:14 +01:00
{
// 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 ( ) ;
2018-01-08 18:41:06 +01:00
// When tx is 0-confirmed or conflicted, posInBlock is SYNC_TRANSACTION_NOT_IN_BLOCK and nHeightNew should be set to -1
int nHeightNew = posInBlock = = CMainSignals : : SYNC_TRANSACTION_NOT_IN_BLOCK ? - 1 : pindex - > nHeight ;
2017-01-29 09:22:14 +01:00
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
2018-07-12 11:07:51 +02:00
for ( const auto & pair : itLockCandidate - > second . mapOutPointLocks ) {
2017-02-04 19:17:45 +01:00
// Check corresponding lock votes
2018-07-12 11:07:51 +02:00
for ( const auto & vote : pair . second . GetVotes ( ) ) {
uint256 nVoteHash = vote . GetHash ( ) ;
2017-02-04 19:17:45 +01:00
LogPrint ( " instantsend " , " CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated \n " ,
txHash . ToString ( ) , nHeightNew , nVoteHash . ToString ( ) ) ;
2018-07-12 11:07:51 +02:00
const auto & it = mapTxLockVotes . find ( nVoteHash ) ;
2017-02-04 19:17:45 +01:00
if ( it ! = mapTxLockVotes . end ( ) ) {
it - > second . SetConfirmedHeight ( nHeightNew ) ;
}
}
}
2017-01-29 09:22:14 +01:00
}
2017-02-04 19:17:45 +01:00
// check orphan votes
2018-07-12 11:07:51 +02:00
for ( const auto & pair : mapTxLockVotesOrphan ) {
if ( pair . second . GetTxHash ( ) = = txHash ) {
2017-02-04 19:17:45 +01:00
LogPrint ( " instantsend " , " CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated \n " ,
2018-07-12 11:07:51 +02:00
txHash . ToString ( ) , nHeightNew , pair . first . ToString ( ) ) ;
mapTxLockVotes [ pair . first ] . SetConfirmedHeight ( nHeightNew ) ;
2017-02-04 19:17:45 +01:00
}
2017-01-29 09:22:14 +01:00
}
2017-02-04 19:17:45 +01:00
}
2017-01-29 09:22:14 +01:00
2018-07-12 11:01:48 +02:00
std : : string CInstantSend : : ToString ( ) const
2017-06-06 01:47:23 +02:00
{
LOCK ( cs_instantsend ) ;
return strprintf ( " Lock Candidates: %llu, Votes %llu " , mapTxLockCandidates . size ( ) , mapTxLockVotes . size ( ) ) ;
}
2017-01-29 09:22:14 +01:00
//
// CTxLockRequest
//
2017-09-14 13:41:40 +02:00
bool CTxLockRequest : : IsValid ( ) const
2017-01-29 09:22:14 +01:00
{
2017-09-20 14:02:53 +02:00
if ( tx - > vout . size ( ) < 1 ) return false ;
2017-01-29 09:22:14 +01:00
2017-09-20 14:02:53 +02:00
if ( tx - > vin . size ( ) > WARN_MANY_INPUTS ) {
2017-02-02 23:38:33 +01:00
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- WARNING: Too many inputs: tx=%s " , ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
}
2018-03-09 15:15:48 +01:00
AssertLockHeld ( cs_main ) ;
2017-09-20 14:02:53 +02:00
if ( ! CheckFinalTx ( * tx ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Transaction is not final: tx=%s " , ToString ( ) ) ;
return false ;
}
2017-03-01 21:30:43 +01:00
CAmount nValueIn = 0 ;
2017-01-29 09:22:14 +01:00
2018-03-10 13:35:09 +01:00
int nInstantSendConfirmationsRequired = Params ( ) . GetConsensus ( ) . nInstantSendConfirmationsRequired ;
2018-02-06 12:09:33 +01:00
for ( const auto & txin : tx - > vin ) {
2017-01-29 09:22:14 +01:00
2017-09-26 16:33:46 +02:00
Coin coin ;
2017-03-01 21:30:43 +01:00
2017-09-26 16:33:46 +02:00
if ( ! GetUTXOCoin ( txin . prevout , coin ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Failed to find UTXO %s \n " , txin . prevout . ToStringShort ( ) ) ;
2017-09-14 13:41:40 +02:00
return false ;
2017-01-29 09:22:14 +01:00
}
2017-09-26 16:33:46 +02:00
int nTxAge = chainActive . Height ( ) - coin . nHeight + 1 ;
2017-01-29 09:22:14 +01:00
// 1 less than the "send IX" gui requires, in case of a block propagating the network at the time
2018-03-10 13:35:09 +01:00
int nConfirmationsRequired = nInstantSendConfirmationsRequired - 1 ;
2017-01-29 09:22:14 +01:00
if ( nTxAge < nConfirmationsRequired ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- outpoint %s too new: nTxAge=%d, nConfirmationsRequired=%d, txid=%s \n " ,
txin . prevout . ToStringShort ( ) , nTxAge , nConfirmationsRequired , GetHash ( ) . ToString ( ) ) ;
return false ;
}
2017-09-26 16:33:46 +02:00
nValueIn + = coin . out . nValue ;
2017-01-29 09:22:14 +01:00
}
2017-09-14 13:41:40 +02:00
if ( nValueIn > sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) * COIN ) {
LogPrint ( " instantsend " , " CTxLockRequest::IsValid -- Transaction value too high: nValueIn=%d, tx=%s " , nValueIn , ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-20 14:02:53 +02:00
CAmount nValueOut = tx - > GetValueOut ( ) ;
2017-12-07 10:43:08 +01:00
2017-01-29 09:22:14 +01:00
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
{
2017-12-07 10:43:23 +01:00
CAmount nMinFee = MIN_FEE ;
2017-09-20 14:02:53 +02:00
return std : : max ( nMinFee , CAmount ( tx - > vin . size ( ) * nMinFee ) ) ;
2017-01-29 09:22:14 +01:00
}
int CTxLockRequest : : GetMaxSignatures ( ) const
{
2017-09-20 14:02:53 +02:00
return tx - > vin . size ( ) * COutPointLock : : SIGNATURES_TOTAL ;
2017-01-29 09:22:14 +01:00
}
//
// CTxLockVote
//
2017-09-19 16:51:38 +02:00
bool CTxLockVote : : IsValid ( CNode * pnode , CConnman & connman ) const
2015-02-01 16:53:49 +01:00
{
2017-09-11 16:13:48 +02:00
if ( ! mnodeman . Has ( outpointMasternode ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Unknown masternode %s \n " , outpointMasternode . ToStringShort ( ) ) ;
2017-09-19 16:51:38 +02:00
mnodeman . AskForMN ( pnode , outpointMasternode , connman ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-26 16:33:46 +02:00
Coin coin ;
if ( ! GetUTXOCoin ( outpoint , coin ) ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Failed to find UTXO %s \n " , outpoint . ToStringShort ( ) ) ;
2017-09-14 13:41:40 +02:00
return false ;
2017-01-29 09:22:14 +01:00
}
2018-03-29 17:08:24 +02:00
int nLockInputHeight = coin . nHeight + Params ( ) . GetConsensus ( ) . nInstantSendConfirmationsRequired - 2 ;
2017-01-29 09:22:14 +01:00
2017-09-14 15:58:29 +02:00
int nRank ;
2018-06-11 12:14:24 +02:00
int nMinRequiredProtocol = std : : max ( MIN_INSTANTSEND_PROTO_VERSION , mnpayments . GetMinMasternodePaymentsProto ( ) ) ;
if ( ! mnodeman . GetMasternodeRank ( outpointMasternode , nRank , nLockInputHeight , nMinRequiredProtocol ) ) {
2017-01-29 09:22:14 +01:00
//can be caused by past versions trying to vote with an invalid protocol
2017-06-06 01:47:23 +02:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Can't calculate rank for masternode %s \n " , outpointMasternode . ToStringShort ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
2017-09-14 15:58:29 +02:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Masternode %s, rank=%d \n " , outpointMasternode . ToStringShort ( ) , nRank ) ;
2017-01-29 09:22:14 +01:00
int nSignaturesTotal = COutPointLock : : SIGNATURES_TOTAL ;
2017-09-14 15:58:29 +02:00
if ( nRank > nSignaturesTotal ) {
2017-01-29 09:22:14 +01:00
LogPrint ( " instantsend " , " CTxLockVote::IsValid -- Masternode %s is not in the top %d (%d), vote hash=%s \n " ,
2017-09-14 15:58:29 +02:00
outpointMasternode . ToStringShort ( ) , nSignaturesTotal , nRank , GetHash ( ) . ToString ( ) ) ;
2017-01-29 09:22:14 +01:00
return false ;
}
if ( ! CheckSignature ( ) ) {
LogPrintf ( " CTxLockVote::IsValid -- Signature invalid \n " ) ;
return false ;
}
return true ;
2015-02-01 16:53:49 +01:00
}
2017-01-29 09:22:14 +01:00
uint256 CTxLockVote : : GetHash ( ) const
{
2018-02-16 15:54:53 +01:00
return SerializeHash ( * this ) ;
}
uint256 CTxLockVote : : GetSignatureHash ( ) const
{
return GetHash ( ) ;
2017-01-29 09:22:14 +01:00
}
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 ;
2014-12-31 03:54:00 +01:00
2017-09-11 16:13:48 +02:00
masternode_info_t infoMn ;
2014-12-09 02:17:57 +01:00
2017-09-11 16:13:48 +02:00
if ( ! mnodeman . GetMasternodeInfo ( outpointMasternode , infoMn ) ) {
2017-01-29 09:22:14 +01:00
LogPrintf ( " CTxLockVote::CheckSignature -- Unknown Masternode: masternode=%s \n " , outpointMasternode . ToString ( ) ) ;
2014-12-09 02:17:57 +01:00
return false ;
}
2018-02-16 15:54:53 +01:00
if ( sporkManager . IsSporkActive ( SPORK_6_NEW_SIGS ) ) {
uint256 hash = GetSignatureHash ( ) ;
if ( ! CHashSigner : : VerifyHash ( hash , infoMn . pubKeyMasternode , vchMasternodeSignature , strError ) ) {
// could be a signature in old format
std : : string strMessage = txHash . ToString ( ) + outpoint . ToStringShort ( ) ;
if ( ! CMessageSigner : : VerifyMessage ( infoMn . pubKeyMasternode , vchMasternodeSignature , strMessage , strError ) ) {
// nope, not in old format either
LogPrintf ( " CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s \n " , strError ) ;
return false ;
}
}
} else {
std : : string strMessage = txHash . ToString ( ) + outpoint . ToStringShort ( ) ;
if ( ! CMessageSigner : : VerifyMessage ( infoMn . pubKeyMasternode , vchMasternodeSignature , strMessage , strError ) ) {
LogPrintf ( " CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s \n " , strError ) ;
return false ;
}
2014-12-09 02:17:57 +01:00
}
return true ;
}
2016-11-17 01:31:35 +01:00
bool CTxLockVote : : Sign ( )
2014-12-09 02:17:57 +01:00
{
2016-08-12 07:55:41 +02:00
std : : string strError ;
2014-12-09 02:17:57 +01:00
2018-02-16 15:54:53 +01:00
if ( sporkManager . IsSporkActive ( SPORK_6_NEW_SIGS ) ) {
uint256 hash = GetSignatureHash ( ) ;
2014-12-09 02:17:57 +01:00
2018-02-16 15:54:53 +01:00
if ( ! CHashSigner : : SignHash ( hash , activeMasternode . keyMasternode , vchMasternodeSignature ) ) {
LogPrintf ( " CTxLockVote::Sign -- SignHash() failed \n " ) ;
return false ;
}
if ( ! CHashSigner : : VerifyHash ( hash , activeMasternode . pubKeyMasternode , vchMasternodeSignature , strError ) ) {
LogPrintf ( " CTxLockVote::Sign -- VerifyHash() failed, error: %s \n " , strError ) ;
return false ;
}
} else {
std : : string strMessage = txHash . ToString ( ) + outpoint . ToStringShort ( ) ;
if ( ! CMessageSigner : : SignMessage ( strMessage , vchMasternodeSignature , activeMasternode . keyMasternode ) ) {
LogPrintf ( " CTxLockVote::Sign -- SignMessage() failed \n " ) ;
return false ;
}
if ( ! CMessageSigner : : VerifyMessage ( activeMasternode . pubKeyMasternode , vchMasternodeSignature , strMessage , strError ) ) {
LogPrintf ( " CTxLockVote::Sign -- VerifyMessage() failed, error: %s \n " , strError ) ;
return false ;
}
2014-12-09 02:17:57 +01:00
}
return true ;
}
2017-09-15 20:05:13 +02:00
void CTxLockVote : : Relay ( CConnman & connman ) const
2017-01-29 09:22:14 +01:00
{
CInv inv ( MSG_TXLOCK_VOTE , GetHash ( ) ) ;
2018-07-20 15:32:41 +02:00
CTxLockRequest request ;
if ( instantsend . GetTxLockRequest ( txHash , request ) )
connman . RelayInvFiltered ( inv , * request . tx ) ;
else
connman . RelayInv ( inv ) ;
2017-01-29 09:22:14 +01:00
}
2014-12-09 02:17:57 +01:00
2017-01-29 09:22:14 +01:00
bool CTxLockVote : : IsExpired ( int nHeight ) const
2014-12-09 02:17:57 +01:00
{
2017-01-29 09:22:14 +01:00
// Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into.
return ( nConfirmedHeight ! = - 1 ) & & ( nHeight - nConfirmedHeight > Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ) ;
}
2014-12-09 02:17:57 +01:00
2017-09-14 13:41:40 +02:00
bool CTxLockVote : : IsTimedOut ( ) const
{
2017-10-31 15:50:03 +01:00
return GetTime ( ) - nTimeCreated > INSTANTSEND_LOCK_TIMEOUT_SECONDS ;
}
bool CTxLockVote : : IsFailed ( ) const
{
return ( GetTime ( ) - nTimeCreated > INSTANTSEND_FAILED_TIMEOUT_SECONDS ) & & ! instantsend . IsLockedInstantSendTransaction ( GetTxHash ( ) ) ;
2017-09-14 13:41:40 +02: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 )
{
2018-07-12 11:08:22 +02:00
return mapMasternodeVotes . emplace ( vote . GetMasternodeOutpoint ( ) , vote ) . second ;
2017-01-29 09:22:14 +01:00
}
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 ;
2018-07-12 11:07:51 +02:00
for ( const auto & pair : mapMasternodeVotes ) {
vRet . push_back ( pair . second ) ;
2017-02-04 19:17:45 +01:00
}
return vRet ;
}
bool COutPointLock : : HasMasternodeVoted ( const COutPoint & outpointMasternodeIn ) const
2017-01-29 09:22:14 +01:00
{
return mapMasternodeVotes . count ( outpointMasternodeIn ) ;
}
2014-12-09 02:17:57 +01:00
2017-09-15 20:05:13 +02:00
void COutPointLock : : Relay ( CConnman & connman ) const
2017-01-29 09:22:14 +01:00
{
2018-07-12 11:07:51 +02:00
for ( const auto & pair : mapMasternodeVotes ) {
pair . second . Relay ( connman ) ;
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 )
{
2018-02-21 20:26:53 +01:00
mapOutPointLocks . insert ( std : : make_pair ( outpoint , COutPointLock ( outpoint ) ) ) ;
2017-01-29 09:22:14 +01:00
}
2017-09-14 13:41:40 +02:00
void CTxLockCandidate : : MarkOutpointAsAttacked ( const COutPoint & outpoint )
{
std : : map < COutPoint , COutPointLock > : : iterator it = mapOutPointLocks . find ( outpoint ) ;
if ( it ! = mapOutPointLocks . end ( ) )
it - > second . MarkAsAttacked ( ) ;
}
2017-01-29 09:22:14 +01:00
bool CTxLockCandidate : : AddVote ( const CTxLockVote & vote )
{
std : : map < COutPoint , COutPointLock > : : iterator it = mapOutPointLocks . find ( vote . GetOutpoint ( ) ) ;
if ( it = = mapOutPointLocks . end ( ) ) return false ;
return it - > second . AddVote ( vote ) ;
2014-12-09 02:17:57 +01:00
}
2017-01-29 09:22:14 +01:00
bool CTxLockCandidate : : IsAllOutPointsReady ( ) const
2014-12-09 02:17:57 +01:00
{
2017-01-29 09:22:14 +01:00
if ( mapOutPointLocks . empty ( ) ) return false ;
2018-07-12 11:07:51 +02:00
for ( const auto & pair : mapOutPointLocks ) {
if ( ! pair . second . IsReady ( ) ) return false ;
2017-01-29 09:22:14 +01:00
}
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 ;
2018-07-12 11:07:51 +02:00
for ( const auto & pair : mapOutPointLocks ) {
nCountVotes + = pair . second . CountVotes ( ) ;
2017-01-29 09:22:14 +01:00
}
return nCountVotes ;
}
2015-02-04 22:09:50 +01:00
2017-01-29 09:22:14 +01:00
bool CTxLockCandidate : : IsExpired ( int nHeight ) const
{
// Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into.
return ( nConfirmedHeight ! = - 1 ) & & ( nHeight - nConfirmedHeight > Params ( ) . GetConsensus ( ) . nInstantSendKeepLock ) ;
}
2016-08-05 21:49:45 +02:00
2017-09-14 13:41:40 +02:00
bool CTxLockCandidate : : IsTimedOut ( ) const
{
2017-10-31 15:50:03 +01:00
return GetTime ( ) - nTimeCreated > INSTANTSEND_LOCK_TIMEOUT_SECONDS ;
2017-09-14 13:41:40 +02:00
}
2017-09-15 20:05:13 +02:00
void CTxLockCandidate : : Relay ( CConnman & connman ) const
2017-01-29 09:22:14 +01:00
{
2017-09-20 14:02:53 +02:00
connman . RelayTransaction ( * txLockRequest . tx ) ;
2018-07-12 11:07:51 +02:00
for ( const auto & pair : mapOutPointLocks ) {
pair . second . Relay ( connman ) ;
2017-01-29 09:22:14 +01:00
}
2014-12-06 20:41:53 +01:00
}