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 ;
CMasternode * winningNode = mnodeman . GetNextMasternodeInQueueForPayment ( nBlockHeight , true , nCount ) ;
2016-08-28 12:11:36 +02:00
if ( ! winningNode ) {
// ...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
2016-09-16 00:00:06 +02:00
payee = GetScriptForDestination ( winningNode - > 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
}
2016-05-24 23:16:42 +02:00
void CMasternodePayments : : ProcessMessage ( CNode * pfrom , std : : string & strCommand , CDataStream & vRecv )
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-03-14 07:22:00 +01:00
Sync ( pfrom ) ;
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
2016-03-02 22:20:04 +01:00
if ( ! pCurrentBlockIndex ) return ;
2015-04-16 21:58:09 +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 ) ) {
LogPrint ( " mnpayments " , " MASTERNODEPAYMENTVOTE -- hash=%s, nHeight=%d seen \n " , nHash . ToString ( ) , pCurrentBlockIndex - > nHeight ) ;
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
}
2016-09-11 22:22:37 +02:00
int nFirstBlock = pCurrentBlockIndex - > nHeight - GetStorageLimit ( ) ;
2016-09-21 16:45:29 +02:00
if ( vote . nBlockHeight < nFirstBlock | | vote . nBlockHeight > pCurrentBlockIndex - > nHeight + 20 ) {
LogPrint ( " mnpayments " , " MASTERNODEPAYMENTVOTE -- vote out of range: nFirstBlock=%d, nBlockHeight=%d, nHeight=%d \n " , nFirstBlock , vote . nBlockHeight , pCurrentBlockIndex - > nHeight ) ;
2015-04-16 21:58:09 +02:00
return ;
}
2015-07-18 21:24:06 +02:00
std : : string strError = " " ;
2016-09-21 16:45:29 +02:00
if ( ! vote . IsValid ( pfrom , pCurrentBlockIndex - > nHeight , strError ) ) {
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 ;
}
2016-12-02 18:15:37 +01:00
masternode_info_t mnInfo = mnodeman . GetMasternodeInfo ( vote . vinMasternode ) ;
if ( ! mnInfo . fInfoValid ) {
// 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 ( ) ) ;
mnodeman . AskForMN ( pfrom , vote . vinMasternode ) ;
return ;
}
int nDos = 0 ;
if ( ! vote . CheckSignature ( mnInfo . pubKeyMasternode , pCurrentBlockIndex - > nHeight , nDos ) ) {
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,
2016-09-21 16:45:29 +02:00
mnodeman . AskForMN ( pfrom , vote . vinMasternode ) ;
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 " ,
address2 . ToString ( ) , vote . nBlockHeight , pCurrentBlockIndex - > nHeight , 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 ) ) {
vote . Relay ( ) ;
masternodeSync . AddedPaymentVote ( ) ;
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 ) ;
2016-03-02 22:20:04 +01:00
if ( ! pCurrentBlockIndex ) 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 ;
2016-03-02 22:20:04 +01:00
for ( int64_t h = pCurrentBlockIndex - > nHeight ; h < = pCurrentBlockIndex - > nHeight + 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 ) ;
}
bool CMasternodeBlockPayees : : HasPayeeWithVotes ( CScript payeeIn , int nVotesReq )
{
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
{
2016-03-02 22:20:04 +01:00
if ( ! pCurrentBlockIndex ) 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
2016-09-21 16:45:29 +02:00
if ( pCurrentBlockIndex - > nHeight - vote . nBlockHeight > nLimit ) {
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
}
2016-09-21 16:45:29 +02:00
bool CMasternodePaymentVote : : IsValid ( CNode * pnode , int nValidationHeight , std : : string & strError )
2015-04-22 16:33:44 +02:00
{
2015-07-21 17:09:17 +02:00
CMasternode * pmn = mnodeman . Find ( vinMasternode ) ;
2015-04-16 21:58:09 +02:00
2016-08-05 18:25:03 +02:00
if ( ! pmn ) {
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 ( ) ) {
2016-08-29 21:11:34 +02:00
mnodeman . AskForMN ( pnode , vinMasternode ) ;
}
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 ;
}
2016-09-16 00:00:06 +02:00
if ( pmn - > nProtocolVersion < nMinRequiredProtocol ) {
strError = strprintf ( " Masternode protocol is too old: nProtocolVersion=%d, nMinRequiredProtocol=%d " , pmn - > 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 ;
2016-11-23 16:30:36 +01:00
int nRank = mnodeman . GetMasternodeRank ( vinMasternode , nBlockHeight - 101 , nMinRequiredProtocol , false ) ;
2015-07-21 17:09:17 +02:00
2017-04-01 19:40:13 +02:00
if ( nRank = = - 1 ) {
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 ) ;
2016-08-05 18:25:03 +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 ;
}
bool CMasternodePayments : : ProcessBlock ( int nBlockHeight )
{
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
2016-11-23 16:30:36 +01:00
int nRank = mnodeman . GetMasternodeRank ( activeMasternode . vin , nBlockHeight - 101 , GetMinMasternodePaymentsProto ( ) , false ) ;
2016-09-08 14:04:45 +02:00
if ( nRank = = - 1 ) {
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
2016-09-15 08:50:01 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock -- Start: nBlockHeight=%d, masternode=%s \n " , nBlockHeight , activeMasternode . vin . prevout . 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 ;
CMasternode * pmn = mnodeman . GetNextMasternodeInQueueForPayment ( nBlockHeight , true , nCount ) ;
2015-04-16 21:58:09 +02:00
2016-09-15 08:50:01 +02:00
if ( pmn = = NULL ) {
LogPrintf ( " CMasternodePayments::ProcessBlock -- ERROR: Failed to find masternode to pay \n " ) ;
return false ;
}
2015-06-25 17:17:53 +02:00
2016-09-15 08:50:01 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock -- Masternode found by GetNextMasternodeInQueueForPayment(): %s \n " , pmn - > vin . prevout . ToStringShort ( ) ) ;
2015-04-22 16:33:44 +02:00
2015-04-16 21:58:09 +02:00
2016-09-16 00:00:06 +02:00
CScript payee = GetScriptForDestination ( pmn - > pubKeyCollateralAddress . GetID ( ) ) ;
2015-04-16 21:58:09 +02:00
2016-09-21 16:45:29 +02:00
CMasternodePaymentVote voteNew ( activeMasternode . vin , 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 ) ) {
voteNew . Relay ( ) ;
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 ;
}
2016-09-21 16:45:29 +02:00
void CMasternodePaymentVote : : Relay ( )
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 ( ) ) ;
Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537)
* net: move CBanDB and CAddrDB out of net.h/cpp
This will eventually solve a circular dependency
* net: Create CConnman to encapsulate p2p connections
* net: Move socket binding into CConnman
* net: move OpenNetworkConnection into CConnman
* net: move ban and addrman functions into CConnman
* net: Add oneshot functions to CConnman
* net: move added node functions to CConnman
* net: Add most functions needed for vNodes to CConnman
* net: handle nodesignals in CConnman
* net: Pass CConnection to wallet rather than using the global
* net: Add rpc error for missing/disabled p2p functionality
* net: Pass CConnman around as needed
* gui: add NodeID to the peer table
* net: create generic functor accessors and move vNodes to CConnman
* net: move whitelist functions into CConnman
* net: move nLastNodeId to CConnman
* net: move nLocalHostNonce to CConnman
This behavior seems to have been quite racy and broken.
Move nLocalHostNonce into CNode, and check received nonces against all
non-fully-connected nodes. If there's a match, assume we've connected
to ourself.
* net: move messageHandlerCondition to CConnman
* net: move send/recv statistics to CConnman
* net: move SendBufferSize/ReceiveFloodSize to CConnman
* net: move nLocalServices/nRelevantServices to CConnman
These are in-turn passed to CNode at connection time. This allows us to offer
different services to different peers (or test the effects of doing so).
* net: move semOutbound and semMasternodeOutbound to CConnman
* net: SocketSendData returns written size
* net: move max/max-outbound to CConnman
* net: Pass best block known height into CConnman
CConnman then passes the current best height into CNode at creation time.
This way CConnman/CNode have no dependency on main for height, and the signals
only move in one direction.
This also helps to prevent identity leakage a tiny bit. Before this change, an
attacker could theoretically make 2 connections on different interfaces. They
would connect fully on one, and only establish the initial connection on the
other. Once they receive a new block, they would relay it to your first
connection, and immediately commence the version handshake on the second. Since
the new block height is reflected immediately, they could attempt to learn
whether the two connections were correlated.
This is, of course, incredibly unlikely to work due to the small timings
involved and receipt from other senders. But it doesn't hurt to lock-in
nBestHeight at the time of connection, rather than letting the remote choose
the time.
* net: pass CClientUIInterface into CConnman
* net: Drop StartNode/StopNode and use CConnman directly
* net: Introduce CConnection::Options to avoid passing so many params
* net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options
* net: move vNodesDisconnected into CConnman
* Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting
* Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead
* net: move MAX_FEELER_CONNECTIONS into connman
2017-07-21 11:35:19 +02:00
g_connman - > RelayInv ( inv ) ;
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
void CMasternodePayments : : Sync ( CNode * pnode )
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
2016-03-02 22:20:04 +01:00
if ( ! pCurrentBlockIndex ) 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-03-14 07:22:00 +01:00
for ( int h = pCurrentBlockIndex - > nHeight ; h < pCurrentBlockIndex - > nHeight + 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-07-27 16:28:05 +02:00
g_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.
2016-09-19 00:23:52 +02:00
void CMasternodePayments : : RequestLowDataPaymentBlocks ( CNode * pnode )
{
2017-01-11 00:00:06 +01:00
if ( ! pCurrentBlockIndex ) 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 ( ) ;
const CBlockIndex * pindex = pCurrentBlockIndex ;
while ( pCurrentBlockIndex - > nHeight - pindex - > nHeight < nLimit ) {
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-07-27 16:28:05 +02:00
g_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-07-27 16:28:05 +02:00
g_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-07-27 16:28:05 +02:00
g_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 ) ;
}
2016-03-02 22:20:04 +01:00
void CMasternodePayments : : UpdatedBlockTip ( const CBlockIndex * pindex )
{
pCurrentBlockIndex = pindex ;
2016-09-15 08:50:01 +02:00
LogPrint ( " mnpayments " , " CMasternodePayments::UpdatedBlockTip -- pCurrentBlockIndex->nHeight=%d \n " , pCurrentBlockIndex - > nHeight ) ;
2016-03-02 22:20:04 +01:00
2016-09-15 08:50:01 +02:00
ProcessBlock ( pindex - > nHeight + 10 ) ;
2016-03-02 22:20:04 +01:00
}