2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 The Dash Core developers
2015-04-16 21:58:09 +02:00
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2016-09-15 08:50:01 +02:00
# include "activemasternode.h"
# include "governance-classes.h"
2015-04-16 21:58:09 +02:00
# include "masternode-payments.h"
2015-07-15 04:44:58 +02:00
# include "masternode-sync.h"
2015-04-16 21:58:09 +02:00
# include "masternodeman.h"
2017-04-12 09:04:06 +02:00
# include "messagesigner.h"
2016-09-27 09:50:04 +02:00
# include "netfulfilledman.h"
2015-05-30 19:27:51 +02:00
# include "spork.h"
2016-09-15 08:50:01 +02:00
# include "util.h"
2015-04-16 21:58:09 +02:00
# include <boost/lexical_cast.hpp>
/** Object for who's going to get paid on which blocks */
2016-02-04 20:29:09 +01:00
CMasternodePayments mnpayments ;
2015-07-21 04:24:43 +02:00
2016-09-12 17:34:11 +02:00
CCriticalSection cs_vecPayees ;
2015-07-29 17:28:49 +02:00
CCriticalSection cs_mapMasternodeBlocks ;
2016-09-21 16:45:29 +02:00
CCriticalSection cs_mapMasternodePaymentVotes ;
2015-07-29 17:28:49 +02:00
2016-08-17 09:08:25 +02:00
/**
* IsBlockValueValid
*
* Determine if coinbase outgoing created money is the correct value
*
* Why is this needed ?
* - In Dash some blocks are superblocks , which output much higher amounts of coins
* - Otherblocks are 10 % lower in outgoing value , so in total , no extra coins are created
* - When non - superblocks are detected , the normal schedule should be maintained
*/
2016-12-01 22:58:55 +01:00
bool IsBlockValueValid ( const CBlock & block , int nBlockHeight , CAmount blockReward , std : : string & strErrorRet )
2016-08-22 03:41:40 +02:00
{
2016-12-01 22:58:55 +01:00
strErrorRet = " " ;
2016-12-08 21:03:46 +01:00
bool isBlockRewardValueMet = ( block . vtx [ 0 ] . GetValueOut ( ) < = blockReward ) ;
2016-08-22 03:41:40 +02:00
if ( fDebug ) LogPrintf ( " block.vtx[0].GetValueOut() %lld <= blockReward %lld \n " , block . vtx [ 0 ] . GetValueOut ( ) , blockReward ) ;
// we are still using budgets, but we have no data about them anymore,
// all we know is predefined budget cycle and window
const Consensus : : Params & consensusParams = Params ( ) . GetConsensus ( ) ;
if ( nBlockHeight < consensusParams . nSuperblockStartBlock ) {
int nOffset = nBlockHeight % consensusParams . nBudgetPaymentsCycleBlocks ;
if ( nBlockHeight > = consensusParams . nBudgetPaymentsStartBlock & &
nOffset < consensusParams . nBudgetPaymentsWindowBlocks ) {
// NOTE: make sure SPORK_13_OLD_SUPERBLOCK_FLAG is disabled when 12.1 starts to go live
if ( masternodeSync . IsSynced ( ) & & ! sporkManager . IsSporkActive ( SPORK_13_OLD_SUPERBLOCK_FLAG ) ) {
// no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled
2016-12-08 21:03:46 +01:00
LogPrint ( " gobject " , " IsBlockValueValid -- Client synced but budget spork is disabled, checking block value against block reward \n " ) ;
if ( ! isBlockRewardValueMet ) {
strErrorRet = strprintf ( " coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, budgets are disabled " ,
2016-12-01 22:58:55 +01:00
nBlockHeight , block . vtx [ 0 ] . GetValueOut ( ) , blockReward ) ;
}
2016-12-08 21:03:46 +01:00
return isBlockRewardValueMet ;
2016-08-17 09:08:25 +02:00
}
2016-08-22 03:41:40 +02:00
LogPrint ( " gobject " , " IsBlockValueValid -- WARNING: Skipping budget block value checks, accepting block \n " ) ;
// TODO: reprocess blocks to make sure they are legit?
return true ;
2016-03-02 21:57:24 +01:00
}
2016-12-08 21:03:46 +01:00
// LogPrint("gobject", "IsBlockValueValid -- Block is not in budget cycle window, checking block value against block reward\n");
if ( ! isBlockRewardValueMet ) {
strErrorRet = strprintf ( " coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, block is not in budget cycle window " ,
2016-12-01 22:58:55 +01:00
nBlockHeight , block . vtx [ 0 ] . GetValueOut ( ) , blockReward ) ;
}
2016-12-08 21:03:46 +01:00
return isBlockRewardValueMet ;
2016-03-02 21:57:24 +01:00
}
2016-08-17 09:08:25 +02:00
2016-08-22 03:41:40 +02:00
// superblocks started
2015-07-04 16:49:49 +02:00
2016-12-08 21:03:46 +01:00
CAmount nSuperblockMaxValue = blockReward + CSuperblock : : GetPaymentsLimit ( nBlockHeight ) ;
bool isSuperblockMaxValueMet = ( block . vtx [ 0 ] . GetValueOut ( ) < = nSuperblockMaxValue ) ;
2015-08-11 23:54:42 +02:00
2016-12-08 21:03:46 +01:00
LogPrint ( " gobject " , " block.vtx[0].GetValueOut() %lld <= nSuperblockMaxValue %lld \n " , block . vtx [ 0 ] . GetValueOut ( ) , nSuperblockMaxValue ) ;
2016-07-30 13:04:27 +02:00
2016-08-17 09:08:25 +02:00
if ( ! masternodeSync . IsSynced ( ) ) {
2016-08-22 03:41:40 +02:00
// not enough data but at least it must NOT exceed superblock max value
if ( CSuperblock : : IsValidBlockHeight ( nBlockHeight ) ) {
if ( fDebug ) LogPrintf ( " IsBlockPayeeValid -- WARNING: Client not synced, checking superblock max bounds only \n " ) ;
2016-12-01 22:58:55 +01:00
if ( ! isSuperblockMaxValueMet ) {
2016-12-08 21:03:46 +01:00
strErrorRet = strprintf ( " coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded superblock max value " ,
nBlockHeight , block . vtx [ 0 ] . GetValueOut ( ) , nSuperblockMaxValue ) ;
2016-12-01 22:58:55 +01:00
}
2016-08-22 03:41:40 +02:00
return isSuperblockMaxValueMet ;
}
2016-12-08 21:03:46 +01:00
if ( ! isBlockRewardValueMet ) {
strErrorRet = strprintf ( " coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, only regular blocks are allowed at this height " ,
2016-12-01 22:58:55 +01:00
nBlockHeight , block . vtx [ 0 ] . GetValueOut ( ) , blockReward ) ;
}
2016-08-22 03:41:40 +02:00
// it MUST be a regular block otherwise
2016-12-08 21:03:46 +01:00
return isBlockRewardValueMet ;
2016-08-17 09:08:25 +02:00
}
2016-08-22 03:41:40 +02:00
// we are synced, let's try to check as much data as we can
2016-08-17 09:08:25 +02:00
2016-09-20 18:24:18 +02:00
if ( sporkManager . IsSporkActive ( SPORK_9_SUPERBLOCKS_ENABLED ) ) {
if ( CSuperblockManager : : IsSuperblockTriggered ( nBlockHeight ) ) {
if ( CSuperblockManager : : IsValid ( block . vtx [ 0 ] , nBlockHeight , blockReward ) ) {
LogPrint ( " gobject " , " IsBlockValueValid -- Valid superblock at height %d: %s " , nBlockHeight , block . vtx [ 0 ] . ToString ( ) ) ;
// all checks are done in CSuperblock::IsValid, nothing to do here
return true ;
}
2016-08-17 09:08:25 +02:00
2016-09-20 18:24:18 +02:00
// triggered but invalid? that's weird
2016-08-22 03:41:40 +02:00
LogPrintf ( " IsBlockValueValid -- ERROR: Invalid superblock detected at height %d: %s " , nBlockHeight , block . vtx [ 0 ] . ToString ( ) ) ;
2016-09-20 18:24:18 +02:00
// should NOT allow invalid superblocks, when superblocks are enabled
2016-12-01 22:58:55 +01:00
strErrorRet = strprintf ( " invalid superblock detected at height %d " , nBlockHeight ) ;
2016-08-22 03:41:40 +02:00
return false ;
}
2016-09-20 18:24:18 +02:00
LogPrint ( " gobject " , " IsBlockValueValid -- No triggered superblock detected at height %d \n " , nBlockHeight ) ;
2016-12-08 21:03:46 +01:00
if ( ! isBlockRewardValueMet ) {
strErrorRet = strprintf ( " coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, no triggered superblock detected " ,
2016-12-01 22:58:55 +01:00
nBlockHeight , block . vtx [ 0 ] . GetValueOut ( ) , blockReward ) ;
}
2016-09-20 18:24:18 +02:00
} else {
// should NOT allow superblocks at all, when superblocks are disabled
2016-09-20 22:26:28 +02:00
LogPrint ( " gobject " , " IsBlockValueValid -- Superblocks are disabled, no superblocks allowed \n " ) ;
2016-12-08 21:03:46 +01:00
if ( ! isBlockRewardValueMet ) {
strErrorRet = strprintf ( " coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, superblocks are disabled " ,
2016-12-01 22:58:55 +01:00
nBlockHeight , block . vtx [ 0 ] . GetValueOut ( ) , blockReward ) ;
}
2015-05-30 19:27:51 +02:00
}
2016-08-22 03:41:40 +02:00
// it MUST be a regular block
2016-12-08 21:03:46 +01:00
return isBlockRewardValueMet ;
2015-05-30 19:27:51 +02:00
}
2016-08-22 03:41:40 +02:00
bool IsBlockPayeeValid ( const CTransaction & txNew , int nBlockHeight , CAmount blockReward )
2015-05-04 17:04:09 +02:00
{
2016-08-22 03:41:40 +02:00
if ( ! masternodeSync . IsSynced ( ) ) {
//there is no budget data to use to check anything, let's just accept the longest chain
if ( fDebug ) LogPrintf ( " IsBlockPayeeValid -- WARNING: Client not synced, skipping block payee checks \n " ) ;
2015-07-18 15:46:54 +02:00
return true ;
}
2016-08-22 03:41:40 +02:00
// we are still using budgets, but we have no data about them anymore,
// we can only check masternode payments
2016-08-17 09:08:25 +02:00
2016-08-22 03:41:40 +02:00
const Consensus : : Params & consensusParams = Params ( ) . GetConsensus ( ) ;
2016-08-17 09:08:25 +02:00
2016-08-22 03:41:40 +02:00
if ( nBlockHeight < consensusParams . nSuperblockStartBlock ) {
if ( mnpayments . IsTransactionValid ( txNew , nBlockHeight ) ) {
LogPrint ( " mnpayments " , " IsBlockPayeeValid -- Valid masternode payment at height %d: %s " , nBlockHeight , txNew . ToString ( ) ) ;
2016-08-17 09:08:25 +02:00
return true ;
2016-08-22 03:41:40 +02:00
}
int nOffset = nBlockHeight % consensusParams . nBudgetPaymentsCycleBlocks ;
if ( nBlockHeight > = consensusParams . nBudgetPaymentsStartBlock & &
nOffset < consensusParams . nBudgetPaymentsWindowBlocks ) {
if ( ! sporkManager . IsSporkActive ( SPORK_13_OLD_SUPERBLOCK_FLAG ) ) {
// no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled
LogPrint ( " gobject " , " IsBlockPayeeValid -- ERROR: Client synced but budget spork is disabled and masternode payment is invalid \n " ) ;
2016-08-17 09:08:25 +02:00
return false ;
}
2016-08-22 03:41:40 +02:00
// NOTE: this should never happen in real, SPORK_13_OLD_SUPERBLOCK_FLAG MUST be disabled when 12.1 starts to go live
LogPrint ( " gobject " , " IsBlockPayeeValid -- WARNING: Probably valid budget block, have no data, accepting \n " ) ;
// TODO: reprocess blocks to make sure they are legit?
return true ;
}
if ( sporkManager . IsSporkActive ( SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT ) ) {
LogPrintf ( " IsBlockPayeeValid -- ERROR: Invalid masternode payment detected at height %d: %s " , nBlockHeight , txNew . ToString ( ) ) ;
return false ;
2016-08-17 09:08:25 +02:00
}
2016-08-22 03:41:40 +02:00
LogPrintf ( " IsBlockPayeeValid -- WARNING: Masternode payment enforcement is disabled, accepting any payee \n " ) ;
return true ;
2016-08-17 09:08:25 +02:00
}
2016-08-22 03:41:40 +02:00
// superblocks started
// SEE IF THIS IS A VALID SUPERBLOCK
2016-08-17 09:08:25 +02:00
2016-09-20 18:24:18 +02:00
if ( sporkManager . IsSporkActive ( SPORK_9_SUPERBLOCKS_ENABLED ) ) {
if ( CSuperblockManager : : IsSuperblockTriggered ( nBlockHeight ) ) {
if ( CSuperblockManager : : IsValid ( txNew , nBlockHeight , blockReward ) ) {
LogPrint ( " gobject " , " IsBlockPayeeValid -- Valid superblock at height %d: %s " , nBlockHeight , txNew . ToString ( ) ) ;
return true ;
}
2015-05-04 17:04:09 +02:00
2016-08-22 03:41:40 +02:00
LogPrintf ( " IsBlockPayeeValid -- ERROR: Invalid superblock detected at height %d: %s " , nBlockHeight , txNew . ToString ( ) ) ;
2016-09-20 18:24:18 +02:00
// should NOT allow such superblocks, when superblocks are enabled
2016-08-22 03:41:40 +02:00
return false ;
}
2016-09-20 18:24:18 +02:00
// continue validation, should pay MN
LogPrint ( " gobject " , " IsBlockPayeeValid -- No triggered superblock detected at height %d \n " , nBlockHeight ) ;
} else {
// should NOT allow superblocks at all, when superblocks are disabled
2016-09-20 22:26:28 +02:00
LogPrint ( " gobject " , " IsBlockPayeeValid -- Superblocks are disabled, no superblocks allowed \n " ) ;
2016-08-22 03:41:40 +02:00
}
2015-05-30 19:27:51 +02:00
2016-08-22 03:41:40 +02:00
// IF THIS ISN'T A SUPERBLOCK OR SUPERBLOCK IS INVALID, IT SHOULD PAY A MASTERNODE DIRECTLY
if ( mnpayments . IsTransactionValid ( txNew , nBlockHeight ) ) {
LogPrint ( " mnpayments " , " IsBlockPayeeValid -- Valid masternode payment at height %d: %s " , nBlockHeight , txNew . ToString ( ) ) ;
return true ;
}
2016-08-17 09:08:25 +02:00
2016-08-22 03:41:40 +02:00
if ( sporkManager . IsSporkActive ( SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT ) ) {
LogPrintf ( " IsBlockPayeeValid -- ERROR: Invalid masternode payment detected at height %d: %s " , nBlockHeight , txNew . ToString ( ) ) ;
return false ;
}
LogPrintf ( " IsBlockPayeeValid -- WARNING: Masternode payment enforcement is disabled, accepting any payee \n " ) ;
return true ;
}
2016-08-17 09:08:25 +02:00
2016-08-28 12:11:36 +02:00
void FillBlockPayments ( CMutableTransaction & txNew , int nBlockHeight , CAmount blockReward , CTxOut & txoutMasternodeRet , std : : vector < CTxOut > & voutSuperblockRet )
2016-08-22 03:41:40 +02:00
{
// only create superblocks if spork is enabled AND if superblock is actually triggered
// (height should be validated inside)
2016-08-29 21:16:02 +02:00
if ( sporkManager . IsSporkActive ( SPORK_9_SUPERBLOCKS_ENABLED ) & &
2016-08-22 03:41:40 +02:00
CSuperblockManager : : IsSuperblockTriggered ( nBlockHeight ) ) {
2016-08-28 12:11:36 +02:00
LogPrint ( " gobject " , " FillBlockPayments -- triggered superblock creation at height %d \n " , nBlockHeight ) ;
CSuperblockManager : : CreateSuperblock ( txNew , nBlockHeight , voutSuperblockRet ) ;
2016-08-22 03:41:40 +02:00
return ;
2016-08-17 09:08:25 +02:00
}
// FILL BLOCK PAYEE WITH MASTERNODE PAYMENT OTHERWISE
2016-08-28 12:11:36 +02:00
mnpayments . FillBlockPayee ( txNew , nBlockHeight , blockReward , txoutMasternodeRet ) ;
LogPrint ( " mnpayments " , " FillBlockPayments -- nBlockHeight %d blockReward %lld txoutMasternodeRet %s txNew %s " ,
nBlockHeight , blockReward , txoutMasternodeRet . ToString ( ) , txNew . ToString ( ) ) ;
2015-05-30 19:27:51 +02:00
}
2015-07-17 17:07:07 +02:00
std : : string GetRequiredPaymentsString ( int nBlockHeight )
2015-05-26 16:56:51 +02:00
{
2016-08-22 03:41:40 +02:00
// IF WE HAVE A ACTIVATED TRIGGER FOR THIS HEIGHT - IT IS A SUPERBLOCK, GET THE REQUIRED PAYEES
2016-08-17 09:08:25 +02:00
if ( CSuperblockManager : : IsSuperblockTriggered ( nBlockHeight ) ) {
return CSuperblockManager : : GetRequiredPaymentsString ( nBlockHeight ) ;
}
// OTHERWISE, PAY MASTERNODE
2016-05-24 23:16:42 +02:00
return mnpayments . GetRequiredPaymentsString ( nBlockHeight ) ;
2015-05-26 16:56:51 +02:00
}
2016-09-15 08:50:01 +02:00
void CMasternodePayments : : Clear ( )
{
2016-09-21 16:45:29 +02:00
LOCK2 ( cs_mapMasternodeBlocks , cs_mapMasternodePaymentVotes ) ;
2016-09-15 08:50:01 +02:00
mapMasternodeBlocks . clear ( ) ;
2016-09-21 16:45:29 +02:00
mapMasternodePaymentVotes . clear ( ) ;
2016-09-15 08:50:01 +02:00
}
2016-09-12 16:59:56 +02:00
bool CMasternodePayments : : CanVote ( COutPoint outMasternode , int nBlockHeight )
{
2016-09-21 16:45:29 +02:00
LOCK ( cs_mapMasternodePaymentVotes ) ;
2016-09-12 16:59:56 +02:00
if ( mapMasternodesLastVote . count ( outMasternode ) & & mapMasternodesLastVote [ outMasternode ] = = nBlockHeight ) {
return false ;
}
//record this masternode voted
mapMasternodesLastVote [ outMasternode ] = nBlockHeight ;
return true ;
}
2016-08-17 09:08:25 +02:00
/**
* FillBlockPayee
*
* Fill Masternode ONLY payment block
*/
2016-08-28 12:11:36 +02:00
void CMasternodePayments : : FillBlockPayee ( CMutableTransaction & txNew , int nBlockHeight , CAmount blockReward , CTxOut & txoutMasternodeRet )
2015-05-30 19:27:51 +02:00
{
2016-08-28 12:11:36 +02:00
// make sure it's not filled yet
txoutMasternodeRet = CTxOut ( ) ;
2015-05-30 19:27:51 +02:00
CScript payee ;
2015-06-25 17:17:53 +02:00
2016-08-22 03:41:40 +02:00
if ( ! mnpayments . GetBlockPayee ( nBlockHeight , payee ) ) {
2016-08-28 12:11:36 +02:00
// no masternode detected...
2016-09-15 08:50:41 +02:00
int nCount = 0 ;
2017-09-11 16:13:48 +02:00
masternode_info_t mnInfo ;
2017-10-17 18:40:25 +02:00
if ( ! mnodeman . GetNextMasternodeInQueueForPayment ( nBlockHeight , true , nCount , mnInfo ) ) {
2016-08-28 12:11:36 +02:00
// ...and we can't calculate it on our own
2016-08-22 03:41:40 +02:00
LogPrintf ( " CMasternodePayments::FillBlockPayee -- Failed to detect masternode to pay \n " ) ;
2016-08-28 12:11:36 +02:00
return ;
2015-05-30 19:27:51 +02:00
}
2016-08-28 12:11:36 +02:00
// fill payee with locally calculated winner and hope for the best
2017-09-11 16:13:48 +02:00
payee = GetScriptForDestination ( mnInfo . pubKeyCollateralAddress . GetID ( ) ) ;
2015-05-30 19:27:51 +02:00
}
2016-08-17 09:08:25 +02:00
// GET MASTERNODE PAYMENT VARIABLES SETUP
2016-08-22 03:41:40 +02:00
CAmount masternodePayment = GetMasternodePayment ( nBlockHeight , blockReward ) ;
2015-05-30 19:27:51 +02:00
2016-08-28 12:11:36 +02:00
// split reward between miner ...
txNew . vout [ 0 ] . nValue - = masternodePayment ;
// ... and masternode
txoutMasternodeRet = CTxOut ( masternodePayment , payee ) ;
txNew . vout . push_back ( txoutMasternodeRet ) ;
2015-05-30 19:27:51 +02:00
2016-08-28 12:11:36 +02:00
CTxDestination address1 ;
ExtractDestination ( payee , address1 ) ;
CBitcoinAddress address2 ( address1 ) ;
2015-05-30 19:27:51 +02:00
2016-08-28 12:11:36 +02:00
LogPrintf ( " CMasternodePayments::FillBlockPayee -- Masternode payment %lld to %s \n " , masternodePayment , address2 . ToString ( ) ) ;
2015-05-30 19:27:51 +02:00
}
2015-07-08 03:57:32 +02:00
int CMasternodePayments : : GetMinMasternodePaymentsProto ( ) {
2016-07-30 13:04:27 +02:00
return sporkManager . IsSporkActive ( SPORK_10_MASTERNODE_PAY_UPDATED_NODES )
2015-07-22 01:11:49 +02:00
? MIN_MASTERNODE_PAYMENT_PROTO_VERSION_2
: MIN_MASTERNODE_PAYMENT_PROTO_VERSION_1 ;
2015-07-08 03:57:32 +02:00
}
2017-09-19 16:51:38 +02:00
void CMasternodePayments : : ProcessMessage ( CNode * pfrom , std : : string & strCommand , CDataStream & vRecv , CConnman & connman )
2015-04-16 21:58:09 +02:00
{
2016-08-29 21:11:34 +02:00
// Ignore any payments messages until masternode list is synced
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) return ;
2015-08-25 00:01:02 +02:00
2016-08-29 21:11:34 +02:00
if ( fLiteMode ) return ; // disable all Dash specific functionality
2015-08-25 00:01:02 +02:00
2016-09-21 16:45:29 +02:00
if ( strCommand = = NetMsgType : : MASTERNODEPAYMENTSYNC ) { //Masternode Payments Request Sync
2016-08-05 18:25:03 +02:00
2016-08-29 21:11:34 +02:00
// Ignore such requests until we are fully synced.
// We could start processing this after masternode list is synced
// but this is a heavy one so it's better to finish sync first.
2016-08-05 18:25:03 +02:00
if ( ! masternodeSync . IsSynced ( ) ) return ;
2015-04-16 21:58:09 +02:00
2015-07-21 00:09:42 +02:00
int nCountNeeded ;
vRecv > > nCountNeeded ;
2016-09-27 09:50:04 +02:00
if ( netfulfilledman . HasFulfilledRequest ( pfrom - > addr , NetMsgType : : MASTERNODEPAYMENTSYNC ) ) {
// Asking for the payments list multiple times in a short period of time is no good
LogPrintf ( " MASTERNODEPAYMENTSYNC -- peer already asked me for the list, peer=%d \n " , pfrom - > id ) ;
Misbehaving ( pfrom - > GetId ( ) , 20 ) ;
return ;
2015-04-16 21:58:09 +02:00
}
2016-09-27 09:50:04 +02:00
netfulfilledman . AddFulfilledRequest ( pfrom - > addr , NetMsgType : : MASTERNODEPAYMENTSYNC ) ;
2015-07-21 04:24:43 +02:00
2017-09-19 16:51:38 +02:00
Sync ( pfrom , connman ) ;
2016-09-21 16:45:29 +02:00
LogPrintf ( " MASTERNODEPAYMENTSYNC -- Sent Masternode payment votes to peer %d \n " , pfrom - > id ) ;
2016-09-15 08:50:01 +02:00
2016-09-21 16:45:29 +02:00
} else if ( strCommand = = NetMsgType : : MASTERNODEPAYMENTVOTE ) { // Masternode Payments Vote for the Winner
2016-09-15 08:50:01 +02:00
2016-09-21 16:45:29 +02:00
CMasternodePaymentVote vote ;
vRecv > > vote ;
2015-04-16 21:58:09 +02:00
2016-09-08 14:04:45 +02:00
if ( pfrom - > nVersion < GetMinMasternodePaymentsProto ( ) ) return ;
2015-08-15 01:19:46 +02:00
2017-01-17 21:02:59 +01:00
uint256 nHash = vote . GetHash ( ) ;
pfrom - > setAskFor . erase ( nHash ) ;
2016-12-02 18:15:37 +01:00
{
LOCK ( cs_mapMasternodePaymentVotes ) ;
2017-01-17 21:02:59 +01:00
if ( mapMasternodePaymentVotes . count ( nHash ) ) {
2017-08-25 14:57:05 +02:00
LogPrint ( " mnpayments " , " MASTERNODEPAYMENTVOTE -- hash=%s, nHeight=%d seen \n " , nHash . ToString ( ) , nCachedBlockHeight ) ;
2016-12-02 18:15:37 +01:00
return ;
}
// Avoid processing same vote multiple times
2017-01-17 21:02:59 +01:00
mapMasternodePaymentVotes [ nHash ] = vote ;
2016-12-02 18:15:37 +01:00
// but first mark vote as non-verified,
// AddPaymentVote() below should take care of it if vote is actually ok
2017-01-17 21:02:59 +01:00
mapMasternodePaymentVotes [ nHash ] . MarkAsNotVerified ( ) ;
2015-04-16 21:58:09 +02:00
}
2017-08-25 14:57:05 +02:00
int nFirstBlock = nCachedBlockHeight - GetStorageLimit ( ) ;
if ( vote . nBlockHeight < nFirstBlock | | vote . nBlockHeight > nCachedBlockHeight + 20 ) {
LogPrint ( " mnpayments " , " MASTERNODEPAYMENTVOTE -- vote out of range: nFirstBlock=%d, nBlockHeight=%d, nHeight=%d \n " , nFirstBlock , vote . nBlockHeight , nCachedBlockHeight ) ;
2015-04-16 21:58:09 +02:00
return ;
}
2015-07-18 21:24:06 +02:00
std : : string strError = " " ;
2017-09-19 16:51:38 +02:00
if ( ! vote . IsValid ( pfrom , nCachedBlockHeight , strError , connman ) ) {
2016-09-21 16:45:29 +02:00
LogPrint ( " mnpayments " , " MASTERNODEPAYMENTVOTE -- invalid message, error: %s \n " , strError ) ;
2015-04-16 21:58:09 +02:00
return ;
}
2016-09-21 16:45:29 +02:00
if ( ! CanVote ( vote . vinMasternode . prevout , vote . nBlockHeight ) ) {
LogPrintf ( " MASTERNODEPAYMENTVOTE -- masternode already voted, masternode=%s \n " , vote . vinMasternode . prevout . ToStringShort ( ) ) ;
2015-07-24 16:12:48 +02:00
return ;
}
2017-09-11 16:13:48 +02:00
masternode_info_t mnInfo ;
if ( ! mnodeman . GetMasternodeInfo ( vote . vinMasternode . prevout , mnInfo ) ) {
2016-12-02 18:15:37 +01:00
// mn was not found, so we can't check vote, some info is probably missing
LogPrintf ( " MASTERNODEPAYMENTVOTE -- masternode is missing %s \n " , vote . vinMasternode . prevout . ToStringShort ( ) ) ;
2017-09-19 16:51:38 +02:00
mnodeman . AskForMN ( pfrom , vote . vinMasternode . prevout , connman ) ;
2016-12-02 18:15:37 +01:00
return ;
}
int nDos = 0 ;
2017-08-25 14:57:05 +02:00
if ( ! vote . CheckSignature ( mnInfo . pubKeyMasternode , nCachedBlockHeight , nDos ) ) {
2016-12-02 18:15:37 +01:00
if ( nDos ) {
LogPrintf ( " MASTERNODEPAYMENTVOTE -- ERROR: invalid signature \n " ) ;
Misbehaving ( pfrom - > GetId ( ) , nDos ) ;
} else {
// only warn about anything non-critical (i.e. nDos == 0) in debug mode
LogPrint ( " mnpayments " , " MASTERNODEPAYMENTVOTE -- WARNING: invalid signature \n " ) ;
2016-08-05 18:25:03 +02:00
}
2016-12-02 18:15:37 +01:00
// Either our info or vote info could be outdated.
// In case our info is outdated, ask for an update,
2017-09-19 16:51:38 +02:00
mnodeman . AskForMN ( pfrom , vote . vinMasternode . prevout , connman ) ;
2016-12-02 18:15:37 +01:00
// but there is nothing we can do if vote info itself is outdated
// (i.e. it was signed by a mn which changed its key),
// so just quit here.
2015-04-16 21:58:09 +02:00
return ;
}
2015-04-22 16:33:44 +02:00
CTxDestination address1 ;
2016-09-21 16:45:29 +02:00
ExtractDestination ( vote . payee , address1 ) ;
2015-04-22 16:33:44 +02:00
CBitcoinAddress address2 ( address1 ) ;
2017-06-06 01:47:23 +02:00
LogPrint ( " mnpayments " , " MASTERNODEPAYMENTVOTE -- vote: address=%s, nBlockHeight=%d, nHeight=%d, prevout=%s, hash=%s new \n " ,
2017-08-25 14:57:05 +02:00
address2 . ToString ( ) , vote . nBlockHeight , nCachedBlockHeight , vote . vinMasternode . prevout . ToStringShort ( ) , nHash . ToString ( ) ) ;
2015-04-16 21:58:09 +02:00
2016-09-21 16:45:29 +02:00
if ( AddPaymentVote ( vote ) ) {
2017-09-19 16:51:38 +02:00
vote . Relay ( connman ) ;
2017-08-09 18:07:03 +02:00
masternodeSync . BumpAssetLastTime ( " MASTERNODEPAYMENTVOTE " ) ;
2015-04-16 21:58:09 +02:00
}
}
}
2016-09-21 16:45:29 +02:00
bool CMasternodePaymentVote : : Sign ( )
2015-04-16 21:58:09 +02:00
{
2016-08-19 13:50:04 +02:00
std : : string strError ;
2016-09-15 08:50:01 +02:00
std : : string strMessage = vinMasternode . prevout . ToStringShort ( ) +
2015-06-25 17:17:53 +02:00
boost : : lexical_cast < std : : string > ( nBlockHeight ) +
2016-02-02 16:28:56 +01:00
ScriptToAsmStr ( payee ) ;
2015-06-25 17:17:53 +02:00
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : SignMessage ( strMessage , vchSig , activeMasternode . keyMasternode ) ) {
2016-09-21 16:45:29 +02:00
LogPrintf ( " CMasternodePaymentVote::Sign -- SignMessage() failed \n " ) ;
2015-04-22 16:33:44 +02:00
return false ;
}
2015-04-16 21:58:09 +02:00
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : VerifyMessage ( activeMasternode . pubKeyMasternode , vchSig , strMessage , strError ) ) {
2016-09-21 16:45:29 +02:00
LogPrintf ( " CMasternodePaymentVote::Sign -- VerifyMessage() failed, error: %s \n " , strError ) ;
2015-04-16 21:58:09 +02:00
return false ;
}
return true ;
}
2015-04-22 16:33:44 +02:00
bool CMasternodePayments : : GetBlockPayee ( int nBlockHeight , CScript & payee )
2015-04-16 21:58:09 +02:00
{
2015-05-28 19:45:31 +02:00
if ( mapMasternodeBlocks . count ( nBlockHeight ) ) {
2016-09-21 16:45:29 +02:00
return mapMasternodeBlocks [ nBlockHeight ] . GetBestPayee ( payee ) ;
2015-04-22 16:33:44 +02:00
}
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
return false ;
}
2015-04-16 21:58:09 +02:00
2016-09-15 08:50:01 +02:00
// Is this masternode scheduled to get paid soon?
2016-09-21 16:45:29 +02:00
// -- Only look ahead up to 8 blocks to allow for propagation of the latest 2 blocks of votes
2015-06-15 02:05:51 +02:00
bool CMasternodePayments : : IsScheduled ( CMasternode & mn , int nNotBlockHeight )
2015-05-27 21:47:01 +02:00
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_mapMasternodeBlocks ) ;
2017-08-25 14:57:05 +02:00
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) return false ;
2015-05-27 21:47:01 +02:00
CScript mnpayee ;
2016-09-16 00:00:06 +02:00
mnpayee = GetScriptForDestination ( mn . pubKeyCollateralAddress . GetID ( ) ) ;
2015-05-27 21:47:01 +02:00
CScript payee ;
2017-08-25 14:57:05 +02:00
for ( int64_t h = nCachedBlockHeight ; h < = nCachedBlockHeight + 8 ; h + + ) {
2015-06-15 02:05:51 +02:00
if ( h = = nNotBlockHeight ) continue ;
2016-09-21 16:45:29 +02:00
if ( mapMasternodeBlocks . count ( h ) & & mapMasternodeBlocks [ h ] . GetBestPayee ( payee ) & & mnpayee = = payee ) {
2016-09-15 08:50:01 +02:00
return true ;
2015-05-27 21:47:01 +02:00
}
}
return false ;
}
2016-09-21 16:45:29 +02:00
bool CMasternodePayments : : AddPaymentVote ( const CMasternodePaymentVote & vote )
2015-04-22 16:33:44 +02:00
{
2016-02-02 16:28:56 +01:00
uint256 blockHash = uint256 ( ) ;
2016-09-21 16:45:29 +02:00
if ( ! GetBlockHash ( blockHash , vote . nBlockHeight - 101 ) ) return false ;
2015-04-16 21:58:09 +02:00
2016-12-02 18:15:37 +01:00
if ( HasVerifiedPaymentVote ( vote . GetHash ( ) ) ) return false ;
2015-04-16 21:58:09 +02:00
2016-12-02 18:15:37 +01:00
LOCK2 ( cs_mapMasternodeBlocks , cs_mapMasternodePaymentVotes ) ;
2015-04-22 16:33:44 +02:00
2016-09-21 16:45:29 +02:00
mapMasternodePaymentVotes [ vote . GetHash ( ) ] = vote ;
2016-09-15 08:50:01 +02:00
2016-09-21 16:45:29 +02:00
if ( ! mapMasternodeBlocks . count ( vote . nBlockHeight ) ) {
CMasternodeBlockPayees blockPayees ( vote . nBlockHeight ) ;
mapMasternodeBlocks [ vote . nBlockHeight ] = blockPayees ;
2015-04-16 21:58:09 +02:00
}
2016-09-21 16:45:29 +02:00
mapMasternodeBlocks [ vote . nBlockHeight ] . AddPayee ( vote ) ;
2015-04-22 16:33:44 +02:00
2015-04-16 21:58:09 +02:00
return true ;
}
2016-12-02 18:15:37 +01:00
bool CMasternodePayments : : HasVerifiedPaymentVote ( uint256 hashIn )
{
LOCK ( cs_mapMasternodePaymentVotes ) ;
std : : map < uint256 , CMasternodePaymentVote > : : iterator it = mapMasternodePaymentVotes . find ( hashIn ) ;
return it ! = mapMasternodePaymentVotes . end ( ) & & it - > second . IsVerified ( ) ;
}
2016-09-21 16:45:29 +02:00
void CMasternodeBlockPayees : : AddPayee ( const CMasternodePaymentVote & vote )
2016-09-12 17:34:11 +02:00
{
LOCK ( cs_vecPayees ) ;
BOOST_FOREACH ( CMasternodePayee & payee , vecPayees ) {
2016-09-21 16:45:29 +02:00
if ( payee . GetPayee ( ) = = vote . payee ) {
payee . AddVoteHash ( vote . GetHash ( ) ) ;
2016-09-12 17:34:11 +02:00
return ;
}
}
2016-09-21 16:45:29 +02:00
CMasternodePayee payeeNew ( vote . payee , vote . GetHash ( ) ) ;
2016-09-12 17:34:11 +02:00
vecPayees . push_back ( payeeNew ) ;
}
2016-09-21 16:45:29 +02:00
bool CMasternodeBlockPayees : : GetBestPayee ( CScript & payeeRet )
2016-09-12 17:34:11 +02:00
{
LOCK ( cs_vecPayees ) ;
if ( ! vecPayees . size ( ) ) {
2016-09-21 16:45:29 +02:00
LogPrint ( " mnpayments " , " CMasternodeBlockPayees::GetBestPayee -- ERROR: couldn't find any payee \n " ) ;
2016-09-12 17:34:11 +02:00
return false ;
}
int nVotes = - 1 ;
BOOST_FOREACH ( CMasternodePayee & payee , vecPayees ) {
2016-09-19 00:23:52 +02:00
if ( payee . GetVoteCount ( ) > nVotes ) {
payeeRet = payee . GetPayee ( ) ;
nVotes = payee . GetVoteCount ( ) ;
2016-09-12 17:34:11 +02:00
}
}
return ( nVotes > - 1 ) ;
}
2017-08-11 20:50:57 +02:00
bool CMasternodeBlockPayees : : HasPayeeWithVotes ( const CScript & payeeIn , int nVotesReq )
2016-09-12 17:34:11 +02:00
{
LOCK ( cs_vecPayees ) ;
BOOST_FOREACH ( CMasternodePayee & payee , vecPayees ) {
2016-09-19 00:23:52 +02:00
if ( payee . GetVoteCount ( ) > = nVotesReq & & payee . GetPayee ( ) = = payeeIn ) {
2016-09-12 17:34:11 +02:00
return true ;
}
}
LogPrint ( " mnpayments " , " CMasternodeBlockPayees::HasPayeeWithVotes -- ERROR: couldn't find any payee with %d+ votes \n " , nVotesReq ) ;
return false ;
}
2015-04-22 16:33:44 +02:00
bool CMasternodeBlockPayees : : IsTransactionValid ( const CTransaction & txNew )
2015-04-16 21:58:09 +02:00
{
2016-09-12 17:34:11 +02:00
LOCK ( cs_vecPayees ) ;
2015-07-29 17:28:49 +02:00
2015-05-04 17:04:09 +02:00
int nMaxSignatures = 0 ;
std : : string strPayeesPossible = " " ;
2016-09-12 17:23:05 +02:00
CAmount nMasternodePayment = GetMasternodePayment ( nBlockHeight , txNew . GetValueOut ( ) ) ;
2015-06-25 20:08:50 +02:00
2016-03-04 06:58:53 +01:00
//require at least MNPAYMENTS_SIGNATURES_REQUIRED signatures
2015-05-04 17:04:09 +02:00
2016-09-12 17:23:05 +02:00
BOOST_FOREACH ( CMasternodePayee & payee , vecPayees ) {
2016-09-19 00:23:52 +02:00
if ( payee . GetVoteCount ( ) > = nMaxSignatures ) {
nMaxSignatures = payee . GetVoteCount ( ) ;
2016-09-12 17:23:05 +02:00
}
}
2015-06-25 17:17:53 +02:00
2016-03-04 06:58:53 +01:00
// if we don't have at least MNPAYMENTS_SIGNATURES_REQUIRED signatures on a payee, approve whichever is the longest chain
2015-06-21 19:06:25 +02:00
if ( nMaxSignatures < MNPAYMENTS_SIGNATURES_REQUIRED ) return true ;
2015-05-04 17:04:09 +02:00
2016-09-12 17:23:05 +02:00
BOOST_FOREACH ( CMasternodePayee & payee , vecPayees ) {
2016-09-19 00:23:52 +02:00
if ( payee . GetVoteCount ( ) > = MNPAYMENTS_SIGNATURES_REQUIRED ) {
2016-09-12 17:23:05 +02:00
BOOST_FOREACH ( CTxOut txout , txNew . vout ) {
2016-09-19 00:23:52 +02:00
if ( payee . GetPayee ( ) = = txout . scriptPubKey & & nMasternodePayment = = txout . nValue ) {
2016-09-12 17:23:05 +02:00
LogPrint ( " mnpayments " , " CMasternodeBlockPayees::IsTransactionValid -- Found required payment \n " ) ;
return true ;
}
2015-06-25 17:17:53 +02:00
}
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
CTxDestination address1 ;
2016-09-19 00:23:52 +02:00
ExtractDestination ( payee . GetPayee ( ) , address1 ) ;
2015-04-22 16:33:44 +02:00
CBitcoinAddress address2 ( address1 ) ;
2015-04-16 21:58:09 +02:00
2016-09-12 17:23:05 +02:00
if ( strPayeesPossible = = " " ) {
strPayeesPossible = address2 . ToString ( ) ;
2015-05-04 17:04:09 +02:00
} else {
2015-06-25 20:08:50 +02:00
strPayeesPossible + = " , " + address2 . ToString ( ) ;
2015-05-04 17:04:09 +02:00
}
2015-04-16 21:58:09 +02:00
}
}
2015-05-04 17:04:09 +02:00
2016-09-12 17:23:05 +02:00
LogPrintf ( " CMasternodeBlockPayees::IsTransactionValid -- ERROR: Missing required payment, possible payees: '%s', amount: %f DASH \n " , strPayeesPossible , ( float ) nMasternodePayment / COIN ) ;
2015-05-04 17:04:09 +02:00
return false ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
std : : string CMasternodeBlockPayees : : GetRequiredPaymentsString ( )
2015-04-16 21:58:09 +02:00
{
2016-09-12 17:34:11 +02:00
LOCK ( cs_vecPayees ) ;
2015-07-29 17:28:49 +02:00
2016-09-12 17:34:11 +02:00
std : : string strRequiredPayments = " Unknown " ;
2015-04-22 16:33:44 +02:00
2016-09-12 17:34:11 +02:00
BOOST_FOREACH ( CMasternodePayee & payee , vecPayees )
2015-04-22 16:33:44 +02:00
{
2015-05-26 16:56:51 +02:00
CTxDestination address1 ;
2016-09-19 00:23:52 +02:00
ExtractDestination ( payee . GetPayee ( ) , address1 ) ;
2015-05-26 16:56:51 +02:00
CBitcoinAddress address2 ( address1 ) ;
2015-04-22 16:33:44 +02:00
2016-09-12 17:34:11 +02:00
if ( strRequiredPayments ! = " Unknown " ) {
2016-09-19 00:23:52 +02:00
strRequiredPayments + = " , " + address2 . ToString ( ) + " : " + boost : : lexical_cast < std : : string > ( payee . GetVoteCount ( ) ) ;
2015-05-26 16:56:51 +02:00
} else {
2016-09-19 00:23:52 +02:00
strRequiredPayments = address2 . ToString ( ) + " : " + boost : : lexical_cast < std : : string > ( payee . GetVoteCount ( ) ) ;
2015-04-16 21:58:09 +02:00
}
}
2015-05-04 17:04:09 +02:00
2016-09-12 17:34:11 +02:00
return strRequiredPayments ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
std : : string CMasternodePayments : : GetRequiredPaymentsString ( int nBlockHeight )
2015-04-16 21:58:09 +02:00
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_mapMasternodeBlocks ) ;
2015-04-22 16:33:44 +02:00
if ( mapMasternodeBlocks . count ( nBlockHeight ) ) {
return mapMasternodeBlocks [ nBlockHeight ] . GetRequiredPaymentsString ( ) ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
return " Unknown " ;
}
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
bool CMasternodePayments : : IsTransactionValid ( const CTransaction & txNew , int nBlockHeight )
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_mapMasternodeBlocks ) ;
2015-04-22 16:33:44 +02:00
if ( mapMasternodeBlocks . count ( nBlockHeight ) ) {
return mapMasternodeBlocks [ nBlockHeight ] . IsTransactionValid ( txNew ) ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
return true ;
2015-04-16 21:58:09 +02:00
}
2016-04-13 19:49:47 +02:00
void CMasternodePayments : : CheckAndRemove ( )
2015-04-16 21:58:09 +02:00
{
2017-08-25 14:57:05 +02:00
if ( ! masternodeSync . IsBlockchainSynced ( ) ) return ;
2015-04-16 21:58:09 +02:00
2016-11-28 15:21:50 +01:00
LOCK2 ( cs_mapMasternodeBlocks , cs_mapMasternodePaymentVotes ) ;
2015-04-16 21:58:09 +02:00
2016-09-15 08:50:01 +02:00
int nLimit = GetStorageLimit ( ) ;
2015-04-16 21:58:09 +02:00
2016-09-21 16:45:29 +02:00
std : : map < uint256 , CMasternodePaymentVote > : : iterator it = mapMasternodePaymentVotes . begin ( ) ;
while ( it ! = mapMasternodePaymentVotes . end ( ) ) {
CMasternodePaymentVote vote = ( * it ) . second ;
2015-06-25 17:17:53 +02:00
2017-08-25 14:57:05 +02:00
if ( nCachedBlockHeight - vote . nBlockHeight > nLimit ) {
2016-09-21 16:45:29 +02:00
LogPrint ( " mnpayments " , " CMasternodePayments::CheckAndRemove -- Removing old Masternode payment: nBlockHeight=%d \n " , vote . nBlockHeight ) ;
mapMasternodePaymentVotes . erase ( it + + ) ;
mapMasternodeBlocks . erase ( vote . nBlockHeight ) ;
2015-05-04 17:04:09 +02:00
} else {
+ + it ;
2015-04-16 21:58:09 +02:00
}
2015-05-04 17:04:09 +02:00
}
2016-09-15 08:50:01 +02:00
LogPrintf ( " CMasternodePayments::CheckAndRemove -- %s \n " , ToString ( ) ) ;
2015-04-16 21:58:09 +02:00
}
2017-09-19 16:51:38 +02:00
bool CMasternodePaymentVote : : IsValid ( CNode * pnode , int nValidationHeight , std : : string & strError , CConnman & connman )
2015-04-22 16:33:44 +02:00
{
2017-09-11 16:13:48 +02:00
masternode_info_t mnInfo ;
2015-04-16 21:58:09 +02:00
2017-09-11 16:13:48 +02:00
if ( ! mnodeman . GetMasternodeInfo ( vinMasternode . prevout , mnInfo ) ) {
2016-08-05 18:25:03 +02:00
strError = strprintf ( " Unknown Masternode: prevout=%s " , vinMasternode . prevout . ToStringShort ( ) ) ;
// Only ask if we are already synced and still have no idea about that Masternode
2017-02-17 01:48:29 +01:00
if ( masternodeSync . IsMasternodeListSynced ( ) ) {
2017-09-19 16:51:38 +02:00
mnodeman . AskForMN ( pnode , vinMasternode . prevout , connman ) ;
2016-08-29 21:11:34 +02:00
}
2015-04-22 16:33:44 +02:00
return false ;
2015-04-16 21:58:09 +02:00
}
2016-09-08 14:04:45 +02:00
int nMinRequiredProtocol ;
2016-12-02 18:15:37 +01:00
if ( nBlockHeight > = nValidationHeight ) {
2016-09-21 16:45:29 +02:00
// new votes must comply SPORK_10_MASTERNODE_PAY_UPDATED_NODES rules
2016-09-08 14:04:45 +02:00
nMinRequiredProtocol = mnpayments . GetMinMasternodePaymentsProto ( ) ;
} else {
// allow non-updated masternodes for old blocks
nMinRequiredProtocol = MIN_MASTERNODE_PAYMENT_PROTO_VERSION_1 ;
}
2017-09-11 16:13:48 +02:00
if ( mnInfo . nProtocolVersion < nMinRequiredProtocol ) {
strError = strprintf ( " Masternode protocol is too old: nProtocolVersion=%d, nMinRequiredProtocol=%d " , mnInfo . nProtocolVersion , nMinRequiredProtocol ) ;
2015-07-21 17:09:17 +02:00
return false ;
}
2016-12-02 18:15:37 +01:00
// Only masternodes should try to check masternode rank for old votes - they need to pick the right winner for future blocks.
// Regular clients (miners included) need to verify masternode rank for future block votes only.
if ( ! fMasterNode & & nBlockHeight < nValidationHeight ) return true ;
2017-09-14 15:58:29 +02:00
int nRank ;
2015-07-21 17:09:17 +02:00
2017-09-14 15:58:29 +02:00
if ( ! mnodeman . GetMasternodeRank ( vinMasternode . prevout , nRank , nBlockHeight - 101 , nMinRequiredProtocol ) ) {
2017-04-01 19:40:13 +02:00
LogPrint ( " mnpayments " , " CMasternodePaymentVote::IsValid -- Can't calculate rank for masternode %s \n " ,
vinMasternode . prevout . ToStringShort ( ) ) ;
return false ;
}
2016-08-05 18:25:03 +02:00
if ( nRank > MNPAYMENTS_SIGNATURES_TOTAL ) {
// It's common to have masternodes mistakenly think they are in the top 10
// We don't want to print all of these messages in normal mode, debug mode should print though
2016-09-15 08:50:01 +02:00
strError = strprintf ( " Masternode is not in the top %d (%d) " , MNPAYMENTS_SIGNATURES_TOTAL , nRank ) ;
2016-08-05 18:25:03 +02:00
// Only ban for new mnw which is out of bounds, for old mnw MN list itself might be way too much off
if ( nRank > MNPAYMENTS_SIGNATURES_TOTAL * 2 & & nBlockHeight > nValidationHeight ) {
2016-09-15 08:50:01 +02:00
strError = strprintf ( " Masternode is not in the top %d (%d) " , MNPAYMENTS_SIGNATURES_TOTAL * 2 , nRank ) ;
2016-09-21 16:45:29 +02:00
LogPrintf ( " CMasternodePaymentVote::IsValid -- Error: %s \n " , strError ) ;
2017-09-14 15:58:29 +02:00
// do not ban nodes before DIP0001 is locked in to avoid banning majority of (old) masternodes
2017-10-04 21:35:09 +02:00
if ( fDIP0001WasLockedIn ) {
2017-09-14 15:58:29 +02:00
Misbehaving ( pnode - > GetId ( ) , 20 ) ;
}
2015-08-28 17:53:08 +02:00
}
2016-08-05 18:25:03 +02:00
// Still invalid however
2015-04-22 16:33:44 +02:00
return false ;
}
return true ;
}
2017-09-19 16:51:38 +02:00
bool CMasternodePayments : : ProcessBlock ( int nBlockHeight , CConnman & connman )
2015-04-22 16:33:44 +02:00
{
2016-05-25 19:06:48 +02:00
// DETERMINE IF WE SHOULD BE VOTING FOR THE NEXT PAYEE
2016-09-15 08:50:01 +02:00
if ( fLiteMode | | ! fMasterNode ) return false ;
2016-08-29 21:11:34 +02:00
2016-09-15 08:50:01 +02:00
// We have little chances to pick the right winner if winners list is out of sync
2016-08-29 21:11:34 +02:00
// but we have no choice, so we'll try. However it doesn't make sense to even try to do so
// if we have not enough data about masternodes.
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) return false ;
2016-09-08 14:04:45 +02:00
2017-09-14 15:58:29 +02:00
int nRank ;
2016-09-08 14:04:45 +02:00
2017-09-14 15:58:29 +02:00
if ( ! mnodeman . GetMasternodeRank ( activeMasternode . outpoint , nRank , nBlockHeight - 101 , GetMinMasternodePaymentsProto ( ) ) ) {
2016-09-08 14:04:45 +02:00
LogPrint ( " mnpayments " , " CMasternodePayments::ProcessBlock -- Unknown Masternode \n " ) ;
return false ;
}
if ( nRank > MNPAYMENTS_SIGNATURES_TOTAL ) {
LogPrint ( " mnpayments " , " CMasternodePayments::ProcessBlock -- Masternode not in the top %d (%d) \n " , MNPAYMENTS_SIGNATURES_TOTAL , nRank ) ;
return false ;
}
2015-04-22 16:33:44 +02:00
2015-04-16 21:58:09 +02:00
2016-05-24 23:16:42 +02:00
// LOCATE THE NEXT MASTERNODE WHICH SHOULD BE PAID
2015-04-22 16:33:44 +02:00
2017-09-11 16:13:48 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock -- Start: nBlockHeight=%d, masternode=%s \n " , nBlockHeight , activeMasternode . outpoint . ToStringShort ( ) ) ;
2015-04-16 21:58:09 +02:00
2016-09-15 08:50:01 +02:00
// pay to the oldest MN that still had no payment but its input is old enough and it was active long enough
int nCount = 0 ;
2017-09-11 16:13:48 +02:00
masternode_info_t mnInfo ;
2015-04-16 21:58:09 +02:00
2017-10-17 18:40:25 +02:00
if ( ! mnodeman . GetNextMasternodeInQueueForPayment ( nBlockHeight , true , nCount , mnInfo ) ) {
2016-09-15 08:50:01 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock -- ERROR: Failed to find masternode to pay \n " ) ;
return false ;
}
2015-06-25 17:17:53 +02:00
2017-09-11 16:13:48 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock -- Masternode found by GetNextMasternodeInQueueForPayment(): %s \n " , mnInfo . vin . prevout . ToStringShort ( ) ) ;
2015-04-22 16:33:44 +02:00
2015-04-16 21:58:09 +02:00
2017-09-11 16:13:48 +02:00
CScript payee = GetScriptForDestination ( mnInfo . pubKeyCollateralAddress . GetID ( ) ) ;
2015-04-16 21:58:09 +02:00
2017-09-11 16:13:48 +02:00
CMasternodePaymentVote voteNew ( activeMasternode . outpoint , nBlockHeight , payee ) ;
2016-05-25 19:06:48 +02:00
2016-09-15 08:50:01 +02:00
CTxDestination address1 ;
ExtractDestination ( payee , address1 ) ;
CBitcoinAddress address2 ( address1 ) ;
2015-04-16 21:58:09 +02:00
2016-09-21 16:45:29 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock -- vote: payee=%s, nBlockHeight=%d \n " , address2 . ToString ( ) , nBlockHeight ) ;
2015-06-22 15:50:33 +02:00
2016-09-15 08:50:01 +02:00
// SIGN MESSAGE TO NETWORK WITH OUR MASTERNODE KEYS
2016-09-21 16:45:29 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock -- Signing vote \n " ) ;
if ( voteNew . Sign ( ) ) {
LogPrintf ( " CMasternodePayments::ProcessBlock -- AddPaymentVote() \n " ) ;
2016-09-15 08:50:01 +02:00
2016-09-21 16:45:29 +02:00
if ( AddPaymentVote ( voteNew ) ) {
2017-09-19 16:51:38 +02:00
voteNew . Relay ( connman ) ;
2015-04-16 21:58:09 +02:00
return true ;
}
}
2015-05-04 17:04:09 +02:00
2015-04-16 21:58:09 +02:00
return false ;
}
2017-10-17 18:42:44 +02:00
void CMasternodePayments : : CheckPreviousBlockVotes ( int nPrevBlockHeight )
{
if ( ! masternodeSync . IsWinnersListSynced ( ) ) return ;
std : : string debugStr ;
debugStr + = strprintf ( " CMasternodePayments::CheckPreviousBlockVotes -- nPrevBlockHeight=%d, expected voting MNs: \n " , nPrevBlockHeight ) ;
CMasternodeMan : : rank_pair_vec_t mns ;
if ( ! mnodeman . GetMasternodeRanks ( mns , nPrevBlockHeight - 101 , GetMinMasternodePaymentsProto ( ) ) ) {
debugStr + = " CMasternodePayments::CheckPreviousBlockVotes -- GetMasternodeRanks failed \n " ;
LogPrint ( " mnpayments " , " %s " , debugStr ) ;
return ;
}
LOCK2 ( cs_mapMasternodeBlocks , cs_mapMasternodePaymentVotes ) ;
for ( int i = 0 ; i < MNPAYMENTS_SIGNATURES_TOTAL & & i < ( int ) mns . size ( ) ; i + + ) {
auto mn = mns [ i ] ;
CScript payee ;
bool found = false ;
if ( mapMasternodeBlocks . count ( nPrevBlockHeight ) ) {
for ( auto & p : mapMasternodeBlocks [ nPrevBlockHeight ] . vecPayees ) {
for ( auto & voteHash : p . GetVoteHashes ( ) ) {
if ( ! mapMasternodePaymentVotes . count ( voteHash ) ) {
debugStr + = strprintf ( " CMasternodePayments::CheckPreviousBlockVotes -- could not find vote %s \n " ,
voteHash . ToString ( ) ) ;
continue ;
}
auto vote = mapMasternodePaymentVotes [ voteHash ] ;
if ( vote . vinMasternode . prevout = = mn . second . vin . prevout ) {
payee = vote . payee ;
found = true ;
break ;
}
}
}
}
if ( ! found ) {
debugStr + = strprintf ( " CMasternodePayments::CheckPreviousBlockVotes -- %s - no vote received \n " ,
mn . second . vin . prevout . ToStringShort ( ) ) ;
mapMasternodesDidNotVote [ mn . second . vin . prevout ] + + ;
continue ;
}
CTxDestination address1 ;
ExtractDestination ( payee , address1 ) ;
CBitcoinAddress address2 ( address1 ) ;
debugStr + = strprintf ( " CMasternodePayments::CheckPreviousBlockVotes -- %s - voted for %s \n " ,
mn . second . vin . prevout . ToStringShort ( ) , address2 . ToString ( ) ) ;
}
debugStr + = " CMasternodePayments::CheckPreviousBlockVotes -- Masternodes which missed a vote in the past: \n " ;
for ( auto it : mapMasternodesDidNotVote ) {
debugStr + = strprintf ( " CMasternodePayments::CheckPreviousBlockVotes -- %s: %d \n " , it . first . ToStringShort ( ) , it . second ) ;
}
LogPrint ( " mnpayments " , " %s " , debugStr ) ;
}
2017-09-19 16:51:38 +02:00
void CMasternodePaymentVote : : Relay ( CConnman & connman )
2015-04-16 21:58:09 +02:00
{
2016-12-02 18:15:37 +01:00
// do not relay until synced
2017-02-17 01:48:29 +01:00
if ( ! masternodeSync . IsWinnersListSynced ( ) ) return ;
2016-09-21 16:45:29 +02:00
CInv inv ( MSG_MASTERNODE_PAYMENT_VOTE , GetHash ( ) ) ;
2017-09-14 15:58:29 +02:00
// relay votes only strictly to new nodes until DIP0001 is locked in to avoid being banned by majority of (old) masternodes
2017-10-04 21:35:09 +02:00
connman . RelayInv ( inv , fDIP0001WasLockedIn ? mnpayments . GetMinMasternodePaymentsProto ( ) : MIN_MASTERNODE_PAYMENT_PROTO_VERSION_2 ) ;
2015-04-16 21:58:09 +02:00
}
2016-12-02 18:15:37 +01:00
bool CMasternodePaymentVote : : CheckSignature ( const CPubKey & pubKeyMasternode , int nValidationHeight , int & nDos )
2015-04-16 21:58:09 +02:00
{
2016-12-02 18:15:37 +01:00
// do not ban by default
nDos = 0 ;
2015-04-16 21:58:09 +02:00
2016-09-15 08:50:01 +02:00
std : : string strMessage = vinMasternode . prevout . ToStringShort ( ) +
boost : : lexical_cast < std : : string > ( nBlockHeight ) +
ScriptToAsmStr ( payee ) ;
2015-04-16 21:58:09 +02:00
2016-09-15 08:50:01 +02:00
std : : string strError = " " ;
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : VerifyMessage ( pubKeyMasternode , vchSig , strMessage , strError ) ) {
2016-12-02 18:15:37 +01:00
// Only ban for future block vote when we are already synced.
// Otherwise it could be the case when MN which signed this vote is using another key now
// and we have no idea about the old one.
2017-02-17 01:48:29 +01:00
if ( masternodeSync . IsMasternodeListSynced ( ) & & nBlockHeight > nValidationHeight ) {
2016-12-02 18:15:37 +01:00
nDos = 20 ;
}
2016-09-21 16:45:29 +02:00
return error ( " CMasternodePaymentVote::CheckSignature -- Got bad Masternode payment signature, masternode=%s, error: %s " , vinMasternode . prevout . ToStringShort ( ) . c_str ( ) , strError ) ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
2016-09-15 08:50:01 +02:00
return true ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
2016-09-21 16:45:29 +02:00
std : : string CMasternodePaymentVote : : ToString ( ) const
2016-09-15 08:50:01 +02:00
{
std : : ostringstream info ;
info < < vinMasternode . prevout . ToStringShort ( ) < <
" , " < < nBlockHeight < <
" , " < < ScriptToAsmStr ( payee ) < <
" , " < < ( int ) vchSig . size ( ) ;
return info . str ( ) ;
}
2017-03-14 07:22:00 +01:00
// Send only votes for future blocks, node should request every other missing payment block individually
2017-09-19 16:51:38 +02:00
void CMasternodePayments : : Sync ( CNode * pnode , CConnman & connman )
2015-04-22 16:33:44 +02:00
{
2016-09-19 00:23:52 +02:00
LOCK ( cs_mapMasternodeBlocks ) ;
2015-05-04 17:04:09 +02:00
2017-08-25 14:57:05 +02:00
if ( ! masternodeSync . IsWinnersListSynced ( ) ) return ;
2015-05-04 17:04:09 +02:00
2015-08-31 06:05:10 +02:00
int nInvCount = 0 ;
2016-09-19 00:23:52 +02:00
2017-08-25 14:57:05 +02:00
for ( int h = nCachedBlockHeight ; h < nCachedBlockHeight + 20 ; h + + ) {
2016-09-19 00:23:52 +02:00
if ( mapMasternodeBlocks . count ( h ) ) {
BOOST_FOREACH ( CMasternodePayee & payee , mapMasternodeBlocks [ h ] . vecPayees ) {
std : : vector < uint256 > vecVoteHashes = payee . GetVoteHashes ( ) ;
BOOST_FOREACH ( uint256 & hash , vecVoteHashes ) {
2016-12-02 18:15:37 +01:00
if ( ! HasVerifiedPaymentVote ( hash ) ) continue ;
2016-09-21 16:45:29 +02:00
pnode - > PushInventory ( CInv ( MSG_MASTERNODE_PAYMENT_VOTE , hash ) ) ;
2016-09-19 00:23:52 +02:00
nInvCount + + ;
}
}
2015-07-23 23:35:14 +02:00
}
2015-05-04 17:04:09 +02:00
}
2016-09-15 08:50:01 +02:00
2016-09-21 16:45:29 +02:00
LogPrintf ( " CMasternodePayments::Sync -- Sent %d votes to peer %d \n " , nInvCount , pnode - > id ) ;
2017-09-19 16:51:38 +02:00
connman . PushMessage ( pnode , NetMsgType : : SYNCSTATUSCOUNT , MASTERNODE_SYNC_MNW , nInvCount ) ;
2015-05-16 04:53:53 +02:00
}
2015-07-21 04:24:43 +02:00
2017-01-11 00:00:06 +01:00
// Request low data/unknown payment blocks in batches directly from some node instead of/after preliminary Sync.
2017-09-19 16:51:38 +02:00
void CMasternodePayments : : RequestLowDataPaymentBlocks ( CNode * pnode , CConnman & connman )
2016-09-19 00:23:52 +02:00
{
2017-08-25 14:57:05 +02:00
if ( ! masternodeSync . IsMasternodeListSynced ( ) ) return ;
2016-09-19 00:23:52 +02:00
2016-11-28 15:21:50 +01:00
LOCK2 ( cs_main , cs_mapMasternodeBlocks ) ;
2016-09-19 00:23:52 +02:00
std : : vector < CInv > vToFetch ;
2017-01-11 00:00:06 +01:00
int nLimit = GetStorageLimit ( ) ;
2017-08-25 14:57:05 +02:00
const CBlockIndex * pindex = chainActive . Tip ( ) ;
2017-01-11 00:00:06 +01:00
2017-08-25 14:57:05 +02:00
while ( nCachedBlockHeight - pindex - > nHeight < nLimit ) {
2017-01-11 00:00:06 +01:00
if ( ! mapMasternodeBlocks . count ( pindex - > nHeight ) ) {
// We have no idea about this block height, let's ask
vToFetch . push_back ( CInv ( MSG_MASTERNODE_PAYMENT_BLOCK , pindex - > GetBlockHash ( ) ) ) ;
// We should not violate GETDATA rules
if ( vToFetch . size ( ) = = MAX_INV_SZ ) {
LogPrintf ( " CMasternodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d blocks \n " , pnode - > id , MAX_INV_SZ ) ;
2017-09-19 16:51:38 +02:00
connman . PushMessage ( pnode , NetMsgType : : GETDATA , vToFetch ) ;
2017-01-11 00:00:06 +01:00
// Start filling new batch
vToFetch . clear ( ) ;
}
}
if ( ! pindex - > pprev ) break ;
pindex = pindex - > pprev ;
}
2016-09-19 00:23:52 +02:00
std : : map < int , CMasternodeBlockPayees > : : iterator it = mapMasternodeBlocks . begin ( ) ;
while ( it ! = mapMasternodeBlocks . end ( ) ) {
int nTotalVotes = 0 ;
bool fFound = false ;
BOOST_FOREACH ( CMasternodePayee & payee , it - > second . vecPayees ) {
if ( payee . GetVoteCount ( ) > = MNPAYMENTS_SIGNATURES_REQUIRED ) {
fFound = true ;
break ;
}
nTotalVotes + = payee . GetVoteCount ( ) ;
}
// A clear winner (MNPAYMENTS_SIGNATURES_REQUIRED+ votes) was found
// or no clear winner was found but there are at least avg number of votes
if ( fFound | | nTotalVotes > = ( MNPAYMENTS_SIGNATURES_TOTAL + MNPAYMENTS_SIGNATURES_REQUIRED ) / 2 ) {
// so just move to the next block
+ + it ;
continue ;
}
// DEBUG
DBG (
// Let's see why this failed
BOOST_FOREACH ( CMasternodePayee & payee , it - > second . vecPayees ) {
CTxDestination address1 ;
2016-10-31 10:11:57 +01:00
ExtractDestination ( payee . GetPayee ( ) , address1 ) ;
2016-09-19 00:23:52 +02:00
CBitcoinAddress address2 ( address1 ) ;
printf ( " payee %s votes %d \n " , address2 . ToString ( ) . c_str ( ) , payee . GetVoteCount ( ) ) ;
}
printf ( " block %d votes total %d \n " , it - > first , nTotalVotes ) ;
)
// END DEBUG
// Low data block found, let's try to sync it
uint256 hash ;
if ( GetBlockHash ( hash , it - > first ) ) {
2016-09-21 16:45:29 +02:00
vToFetch . push_back ( CInv ( MSG_MASTERNODE_PAYMENT_BLOCK , hash ) ) ;
2016-09-19 00:23:52 +02:00
}
// We should not violate GETDATA rules
if ( vToFetch . size ( ) = = MAX_INV_SZ ) {
2017-02-19 22:02:33 +01:00
LogPrintf ( " CMasternodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d payment blocks \n " , pnode - > id , MAX_INV_SZ ) ;
2017-09-19 16:51:38 +02:00
connman . PushMessage ( pnode , NetMsgType : : GETDATA , vToFetch ) ;
2016-09-19 00:23:52 +02:00
// Start filling new batch
vToFetch . clear ( ) ;
}
+ + it ;
}
// Ask for the rest of it
if ( ! vToFetch . empty ( ) ) {
2017-02-19 22:02:33 +01:00
LogPrintf ( " CMasternodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d payment blocks \n " , pnode - > id , vToFetch . size ( ) ) ;
2017-09-19 16:51:38 +02:00
connman . PushMessage ( pnode , NetMsgType : : GETDATA , vToFetch ) ;
2016-09-19 00:23:52 +02:00
}
}
2015-07-21 04:24:43 +02:00
std : : string CMasternodePayments : : ToString ( ) const
{
std : : ostringstream info ;
2016-09-21 16:45:29 +02:00
info < < " Votes: " < < ( int ) mapMasternodePaymentVotes . size ( ) < <
2015-07-21 04:24:43 +02:00
" , Blocks: " < < ( int ) mapMasternodeBlocks . size ( ) ;
return info . str ( ) ;
}
2016-09-21 17:32:42 +02:00
bool CMasternodePayments : : IsEnoughData ( )
{
float nAverageVotes = ( MNPAYMENTS_SIGNATURES_TOTAL + MNPAYMENTS_SIGNATURES_REQUIRED ) / 2 ;
int nStorageLimit = GetStorageLimit ( ) ;
return GetBlockCount ( ) > nStorageLimit & & GetVoteCount ( ) > nStorageLimit * nAverageVotes ;
2016-05-29 20:35:09 +02:00
}
2016-09-11 22:22:37 +02:00
int CMasternodePayments : : GetStorageLimit ( )
{
return std : : max ( int ( mnodeman . size ( ) * nStorageCoeff ) , nMinBlocksToStore ) ;
}
2017-09-19 16:51:38 +02:00
void CMasternodePayments : : UpdatedBlockTip ( const CBlockIndex * pindex , CConnman & connman )
2016-03-02 22:20:04 +01:00
{
2017-08-25 14:57:05 +02:00
if ( ! pindex ) return ;
nCachedBlockHeight = pindex - > nHeight ;
LogPrint ( " mnpayments " , " CMasternodePayments::UpdatedBlockTip -- nCachedBlockHeight=%d \n " , nCachedBlockHeight ) ;
2016-03-02 22:20:04 +01:00
2017-10-17 18:42:44 +02:00
int nFutureBlock = nCachedBlockHeight + 10 ;
CheckPreviousBlockVotes ( nFutureBlock - 1 ) ;
ProcessBlock ( nFutureBlock , connman ) ;
2016-03-02 22:20:04 +01:00
}