2015-04-16 21:58:09 +02:00
// Copyright (c) 2014-2015 The Dash developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include "masternode-payments.h"
2015-05-04 17:04:09 +02:00
# include "masternode-budget.h"
2015-07-15 04:44:58 +02:00
# include "masternode-sync.h"
2015-04-16 21:58:09 +02:00
# include "masternodeman.h"
# include "darksend.h"
# include "util.h"
# include "sync.h"
2015-05-30 19:27:51 +02:00
# include "spork.h"
2015-04-16 21:58:09 +02:00
# include "addrman.h"
# include <boost/lexical_cast.hpp>
2015-07-21 04:24:43 +02:00
# include <boost/filesystem.hpp>
2015-04-16 21:58:09 +02:00
/** Object for who's going to get paid on which blocks */
CMasternodePayments masternodePayments ;
2015-07-21 04:24:43 +02:00
2015-07-29 17:28:49 +02:00
CCriticalSection cs_vecPayments ;
CCriticalSection cs_mapMasternodeBlocks ;
CCriticalSection cs_mapMasternodePayeeVotes ;
2015-07-21 04:24:43 +02:00
//
// CMasternodePaymentDB
//
CMasternodePaymentDB : : CMasternodePaymentDB ( )
{
pathDB = GetDataDir ( ) / " mnpayments.dat " ;
strMagicMessage = " MasternodePayments " ;
}
bool CMasternodePaymentDB : : Write ( const CMasternodePayments & objToSave )
{
int64_t nStart = GetTimeMillis ( ) ;
// serialize, checksum data up to that point, then append checksum
CDataStream ssObj ( SER_DISK , CLIENT_VERSION ) ;
ssObj < < strMagicMessage ; // masternode cache file specific magic message
ssObj < < FLATDATA ( Params ( ) . MessageStart ( ) ) ; // network specific magic number
ssObj < < objToSave ;
uint256 hash = Hash ( ssObj . begin ( ) , ssObj . end ( ) ) ;
ssObj < < hash ;
// open output file, and associate with CAutoFile
FILE * file = fopen ( pathDB . string ( ) . c_str ( ) , " wb " ) ;
CAutoFile fileout ( file , SER_DISK , CLIENT_VERSION ) ;
if ( fileout . IsNull ( ) )
return error ( " %s : Failed to open file %s " , __func__ , pathDB . string ( ) ) ;
// Write and commit header, data
try {
fileout < < ssObj ;
}
catch ( std : : exception & e ) {
return error ( " %s : Serialize or I/O error - %s " , __func__ , e . what ( ) ) ;
}
fileout . fclose ( ) ;
LogPrintf ( " Written info to mnpayments.dat %dms \n " , GetTimeMillis ( ) - nStart ) ;
return true ;
}
CMasternodePaymentDB : : ReadResult CMasternodePaymentDB : : Read ( CMasternodePayments & objToLoad , bool fDryRun )
{
int64_t nStart = GetTimeMillis ( ) ;
// open input file, and associate with CAutoFile
FILE * file = fopen ( pathDB . string ( ) . c_str ( ) , " rb " ) ;
CAutoFile filein ( file , SER_DISK , CLIENT_VERSION ) ;
if ( filein . IsNull ( ) )
{
error ( " %s : Failed to open file %s " , __func__ , pathDB . string ( ) ) ;
return FileError ;
}
// use file size to size memory buffer
int fileSize = boost : : filesystem : : file_size ( pathDB ) ;
int dataSize = fileSize - sizeof ( uint256 ) ;
// Don't try to resize to a negative number if file is small
if ( dataSize < 0 )
dataSize = 0 ;
vector < unsigned char > vchData ;
vchData . resize ( dataSize ) ;
uint256 hashIn ;
// read data and checksum from file
try {
filein . read ( ( char * ) & vchData [ 0 ] , dataSize ) ;
filein > > hashIn ;
}
catch ( std : : exception & e ) {
error ( " %s : Deserialize or I/O error - %s " , __func__ , e . what ( ) ) ;
return HashReadError ;
}
filein . fclose ( ) ;
CDataStream ssObj ( vchData , SER_DISK , CLIENT_VERSION ) ;
// verify stored checksum matches input data
uint256 hashTmp = Hash ( ssObj . begin ( ) , ssObj . end ( ) ) ;
if ( hashIn ! = hashTmp )
{
error ( " %s : Checksum mismatch, data corrupted " , __func__ ) ;
return IncorrectHash ;
}
unsigned char pchMsgTmp [ 4 ] ;
std : : string strMagicMessageTmp ;
try {
// de-serialize file header (masternode cache file specific magic message) and ..
ssObj > > strMagicMessageTmp ;
// ... verify the message matches predefined one
if ( strMagicMessage ! = strMagicMessageTmp )
{
error ( " %s : Invalid masternode payement cache magic message " , __func__ ) ;
return IncorrectMagicMessage ;
}
// de-serialize file header (network specific magic number) and ..
ssObj > > FLATDATA ( pchMsgTmp ) ;
// ... verify the network matches ours
if ( memcmp ( pchMsgTmp , Params ( ) . MessageStart ( ) , sizeof ( pchMsgTmp ) ) )
{
error ( " %s : Invalid network magic number " , __func__ ) ;
return IncorrectMagicNumber ;
}
// de-serialize data into CMasternodePayments object
ssObj > > objToLoad ;
}
catch ( std : : exception & e ) {
objToLoad . Clear ( ) ;
error ( " %s : Deserialize or I/O error - %s " , __func__ , e . what ( ) ) ;
return IncorrectFormat ;
}
LogPrintf ( " Loaded info from mnpayments.dat %dms \n " , GetTimeMillis ( ) - nStart ) ;
LogPrintf ( " %s \n " , objToLoad . ToString ( ) ) ;
if ( ! fDryRun ) {
2015-07-30 16:41:14 +02:00
LogPrintf ( " Masternode payments manager - cleaning.... \n " ) ;
objToLoad . CleanPaymentList ( ) ;
2015-07-21 04:24:43 +02:00
LogPrintf ( " Masternode payments manager - result: \n " ) ;
LogPrintf ( " %s \n " , objToLoad . ToString ( ) ) ;
}
return Ok ;
}
void DumpMasternodePayments ( )
{
int64_t nStart = GetTimeMillis ( ) ;
CMasternodePaymentDB paymentdb ;
CMasternodePayments tempPayments ;
LogPrintf ( " Verifying mnpayments.dat format... \n " ) ;
CMasternodePaymentDB : : ReadResult readResult = paymentdb . Read ( tempPayments , true ) ;
// there was an error and it was not an error on file opening => do not proceed
if ( readResult = = CMasternodePaymentDB : : FileError )
LogPrintf ( " Missing budgets file - mnpayments.dat, will try to recreate \n " ) ;
else if ( readResult ! = CMasternodePaymentDB : : Ok )
{
LogPrintf ( " Error reading mnpayments.dat: " ) ;
if ( readResult = = CMasternodePaymentDB : : IncorrectFormat )
LogPrintf ( " magic is ok but data has invalid format, will try to recreate \n " ) ;
else
{
LogPrintf ( " file format is unknown or invalid, please fix it manually \n " ) ;
return ;
}
}
LogPrintf ( " Writting info to mnpayments.dat... \n " ) ;
paymentdb . Write ( masternodePayments ) ;
LogPrintf ( " Budget dump finished %dms \n " , GetTimeMillis ( ) - nStart ) ;
}
2015-04-16 21:58:09 +02:00
2016-01-24 05:21:14 +01:00
bool IsBlockValueValid ( const CBlock & block , CAmount nExpectedValue ) {
2015-05-30 19:27:51 +02:00
CBlockIndex * pindexPrev = chainActive . Tip ( ) ;
if ( pindexPrev = = NULL ) return true ;
2015-07-04 16:49:49 +02:00
int nHeight = 0 ;
if ( pindexPrev - > GetBlockHash ( ) = = block . hashPrevBlock )
{
nHeight = pindexPrev - > nHeight + 1 ;
} else { //out of order
BlockMap : : iterator mi = mapBlockIndex . find ( block . hashPrevBlock ) ;
if ( mi ! = mapBlockIndex . end ( ) & & ( * mi ) . second )
nHeight = ( * mi ) . second - > nHeight + 1 ;
}
if ( nHeight = = 0 ) {
LogPrintf ( " IsBlockValueValid() : WARNING: Couldn't find previous block " ) ;
}
2015-07-17 05:03:42 +02:00
if ( ! masternodeSync . IsSynced ( ) ) { //there is no budget data to use to check anything
2015-05-30 19:27:51 +02:00
//super blocks will always be on these blocks, max 100 per budgeting
2015-07-04 16:49:49 +02:00
if ( nHeight % GetBudgetPaymentCycleBlocks ( ) < 100 ) {
2015-05-30 19:27:51 +02:00
return true ;
} else {
2015-07-04 16:49:49 +02:00
if ( block . vtx [ 0 ] . GetValueOut ( ) > nExpectedValue ) return false ;
2015-05-30 19:27:51 +02:00
}
2015-07-02 01:46:03 +02:00
} else { // we're synced and have data so check the budget schedule
2015-08-11 23:54:42 +02:00
//are these blocks even enabled
if ( ! IsSporkActive ( SPORK_13_ENABLE_SUPERBLOCKS ) ) {
return block . vtx [ 0 ] . GetValueOut ( ) < = nExpectedValue ;
}
2015-07-04 16:49:49 +02:00
if ( budget . IsBudgetPaymentBlock ( nHeight ) ) {
2015-05-30 19:27:51 +02:00
//the value of the block is evaluated in CheckBlock
return true ;
} else {
2015-07-04 16:49:49 +02:00
if ( block . vtx [ 0 ] . GetValueOut ( ) > nExpectedValue ) return false ;
2015-05-30 19:27:51 +02:00
}
}
return true ;
}
2015-07-17 17:07:07 +02:00
bool IsBlockPayeeValid ( const CTransaction & txNew , int nBlockHeight )
2015-05-04 17:04:09 +02:00
{
2015-07-18 15:46:54 +02:00
if ( ! masternodeSync . IsSynced ( ) ) { //there is no budget data to use to check anything -- find the longest chain
2015-07-31 17:46:47 +02:00
LogPrint ( " mnpayments " , " Client not synced, skipping block payee checks \n " ) ;
2015-07-18 15:46:54 +02:00
return true ;
}
2015-05-04 17:04:09 +02:00
//check if it's a budget block
2015-08-03 20:37:58 +02:00
if ( IsSporkActive ( SPORK_13_ENABLE_SUPERBLOCKS ) ) {
if ( budget . IsBudgetPaymentBlock ( nBlockHeight ) ) {
if ( budget . IsTransactionValid ( txNew , nBlockHeight ) ) {
2015-05-30 19:27:51 +02:00
return true ;
2015-08-03 20:37:58 +02:00
} else {
LogPrintf ( " Invalid budget payment detected %s \n " , txNew . ToString ( ) . c_str ( ) ) ;
if ( IsSporkActive ( SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT ) ) {
return false ;
} else {
LogPrintf ( " Budget enforcement is disabled, accepting block \n " ) ;
return true ;
}
2015-05-30 19:27:51 +02:00
}
2015-05-04 17:04:09 +02:00
}
}
//check for masternode payee
if ( masternodePayments . IsTransactionValid ( txNew , nBlockHeight ) )
{
return true ;
2015-05-30 19:27:51 +02:00
} else {
LogPrintf ( " Invalid mn payment detected %s \n " , txNew . ToString ( ) . c_str ( ) ) ;
if ( IsSporkActive ( SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT ) ) {
return false ;
} else {
2015-06-23 22:44:20 +02:00
LogPrintf ( " Masternode payment enforcement is disabled, accepting block \n " ) ;
2015-05-30 19:27:51 +02:00
return true ;
}
2015-05-04 17:04:09 +02:00
}
return false ;
}
2015-05-30 19:27:51 +02:00
2016-01-24 05:21:14 +01:00
void FillBlockPayee ( CMutableTransaction & txNew , CAmount nFees )
2015-05-30 19:27:51 +02:00
{
CBlockIndex * pindexPrev = chainActive . Tip ( ) ;
if ( ! pindexPrev ) return ;
2015-06-25 17:17:53 +02:00
2015-08-03 20:37:58 +02:00
if ( IsSporkActive ( SPORK_13_ENABLE_SUPERBLOCKS ) & & budget . IsBudgetPaymentBlock ( pindexPrev - > nHeight + 1 ) ) {
2015-05-30 19:27:51 +02:00
budget . FillBlockPayee ( txNew , nFees ) ;
} else {
masternodePayments . FillBlockPayee ( txNew , nFees ) ;
}
}
2015-07-17 17:07:07 +02:00
std : : string GetRequiredPaymentsString ( int nBlockHeight )
2015-05-26 16:56:51 +02:00
{
2015-08-03 20:37:58 +02:00
if ( IsSporkActive ( SPORK_13_ENABLE_SUPERBLOCKS ) & & budget . IsBudgetPaymentBlock ( nBlockHeight ) ) {
2015-05-26 16:56:51 +02:00
return budget . GetRequiredPaymentsString ( nBlockHeight ) ;
} else {
return masternodePayments . GetRequiredPaymentsString ( nBlockHeight ) ;
}
}
2016-01-24 05:21:14 +01:00
void CMasternodePayments : : FillBlockPayee ( CMutableTransaction & txNew , CAmount nFees )
2015-05-30 19:27:51 +02:00
{
CBlockIndex * pindexPrev = chainActive . Tip ( ) ;
if ( ! pindexPrev ) return ;
bool hasPayment = true ;
CScript payee ;
2015-06-25 17:17:53 +02:00
2015-05-30 19:27:51 +02:00
//spork
if ( ! masternodePayments . GetBlockPayee ( pindexPrev - > nHeight + 1 , payee ) ) {
//no masternode detected
CMasternode * winningNode = mnodeman . GetCurrentMasterNode ( 1 ) ;
if ( winningNode ) {
payee = GetScriptForDestination ( winningNode - > pubkey . GetID ( ) ) ;
} else {
LogPrintf ( " CreateNewBlock: Failed to detect masternode to pay \n " ) ;
hasPayment = false ;
}
}
CAmount blockValue = GetBlockValue ( pindexPrev - > nBits , pindexPrev - > nHeight , nFees ) ;
CAmount masternodePayment = GetMasternodePayment ( pindexPrev - > nHeight + 1 , blockValue ) ;
2015-06-25 17:17:53 +02:00
2015-05-30 19:27:51 +02:00
txNew . vout [ 0 ] . nValue = blockValue ;
if ( hasPayment ) {
txNew . vout . resize ( 2 ) ;
txNew . vout [ 1 ] . scriptPubKey = payee ;
txNew . vout [ 1 ] . nValue = masternodePayment ;
txNew . vout [ 0 ] . nValue - = masternodePayment ;
CTxDestination address1 ;
ExtractDestination ( payee , address1 ) ;
CBitcoinAddress address2 ( address1 ) ;
LogPrintf ( " Masternode payment to %s \n " , address2 . ToString ( ) . c_str ( ) ) ;
}
}
2015-07-08 03:57:32 +02:00
int CMasternodePayments : : GetMinMasternodePaymentsProto ( ) {
2015-07-27 17:27:58 +02:00
return 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
}
2015-04-22 16:33:44 +02:00
void CMasternodePayments : : ProcessMessageMasternodePayments ( CNode * pfrom , std : : string & strCommand , CDataStream & vRecv )
2015-04-16 21:58:09 +02:00
{
2015-08-07 06:48:55 +02:00
if ( ! masternodeSync . IsBlockchainSynced ( ) ) return ;
2015-04-16 21:58:09 +02:00
2015-08-25 00:01:02 +02:00
if ( fLiteMode ) return ; //disable all Darksend/Masternode related functionality
2015-04-16 21:58:09 +02:00
if ( strCommand = = " mnget " ) { //Masternode Payments Request Sync
if ( fLiteMode ) return ; //disable all Darksend/Masternode related functionality
2015-07-21 00:09:42 +02:00
int nCountNeeded ;
vRecv > > nCountNeeded ;
2015-07-21 04:24:43 +02:00
if ( Params ( ) . NetworkID ( ) = = CBaseChainParams : : MAIN ) {
if ( pfrom - > HasFulfilledRequest ( " mnget " ) ) {
LogPrintf ( " mnget - peer already asked me for the list \n " ) ;
Misbehaving ( pfrom - > GetId ( ) , 20 ) ;
return ;
}
2015-04-16 21:58:09 +02:00
}
2015-07-21 04:24:43 +02:00
2015-04-16 21:58:09 +02:00
pfrom - > FulfilledRequest ( " mnget " ) ;
2015-07-21 00:09:42 +02:00
masternodePayments . Sync ( pfrom , nCountNeeded ) ;
2015-04-16 21:58:09 +02:00
LogPrintf ( " mnget - Sent Masternode winners to %s \n " , pfrom - > addr . ToString ( ) . c_str ( ) ) ;
}
else if ( strCommand = = " mnw " ) { //Masternode Payments Declare Winner
2015-05-27 21:47:01 +02:00
//this is required in litemodef
2015-04-16 21:58:09 +02:00
CMasternodePaymentWinner winner ;
vRecv > > winner ;
2015-08-15 01:19:46 +02:00
if ( pfrom - > nVersion < MIN_MNW_PEER_PROTO_VERSION ) return ;
2015-04-16 21:58:09 +02:00
if ( chainActive . Tip ( ) = = NULL ) return ;
2015-07-21 04:24:43 +02:00
if ( masternodePayments . mapMasternodePayeeVotes . count ( winner . GetHash ( ) ) ) {
2015-07-31 17:46:47 +02:00
LogPrint ( " mnpayments " , " mnw - Already seen - %s bestHeight %d \n " , winner . GetHash ( ) . ToString ( ) . c_str ( ) , chainActive . Tip ( ) - > nHeight ) ;
2015-08-05 02:54:02 +02:00
masternodeSync . AddedMasternodeWinner ( winner . GetHash ( ) ) ;
2015-07-30 18:00:28 +02:00
return ;
2015-04-16 21:58:09 +02:00
}
2015-09-04 14:22:17 +02:00
int nFirstBlock = chainActive . Tip ( ) - > nHeight - ( mnodeman . CountEnabled ( ) * 1.25 ) ;
2015-07-21 00:09:42 +02:00
if ( winner . nBlockHeight < nFirstBlock | | winner . nBlockHeight > chainActive . Tip ( ) - > nHeight + 20 ) {
2015-09-04 14:22:17 +02:00
LogPrint ( " mnpayments " , " mnw - winner out of range - FirstBlock %d Height %d bestHeight %d \n " , nFirstBlock , winner . nBlockHeight , chainActive . Tip ( ) - > nHeight ) ;
2015-04-16 21:58:09 +02:00
return ;
}
2015-07-18 21:24:06 +02:00
std : : string strError = " " ;
2015-08-30 01:48:19 +02:00
if ( ! winner . IsValid ( pfrom , strError ) ) {
2015-08-28 17:53:08 +02:00
if ( strError ! = " " ) LogPrintf ( " mnw - invalid message - %s \n " , strError ) ;
2015-04-16 21:58:09 +02:00
return ;
}
2015-07-24 16:12:48 +02:00
if ( ! masternodePayments . CanVote ( winner . vinMasternode . prevout , winner . nBlockHeight ) ) {
LogPrintf ( " mnw - masternode already voted - %s \n " , winner . vinMasternode . prevout . ToStringShort ( ) ) ;
return ;
}
2015-04-22 16:33:44 +02:00
if ( ! winner . SignatureValid ( ) ) {
2015-04-16 21:58:09 +02:00
LogPrintf ( " mnw - invalid signature \n " ) ;
2015-10-02 09:21:49 +02:00
if ( masternodeSync . IsSynced ( ) ) Misbehaving ( pfrom - > GetId ( ) , 20 ) ;
2015-08-07 05:07:40 +02:00
// it could just be a non-synced masternode
mnodeman . AskForMN ( pfrom , winner . vinMasternode ) ;
2015-04-16 21:58:09 +02:00
return ;
}
2015-04-22 16:33:44 +02:00
CTxDestination address1 ;
2015-06-25 20:08:50 +02:00
ExtractDestination ( winner . payee , address1 ) ;
2015-04-22 16:33:44 +02:00
CBitcoinAddress address2 ( address1 ) ;
2015-07-31 17:46:47 +02:00
LogPrint ( " mnpayments " , " mnw - winning vote - Addr %s Height %d bestHeight %d - %s \n " , address2 . ToString ( ) . c_str ( ) , winner . nBlockHeight , chainActive . Tip ( ) - > nHeight , winner . vinMasternode . prevout . ToStringShort ( ) ) ;
2015-04-16 21:58:09 +02:00
2015-08-30 01:48:19 +02:00
if ( masternodePayments . AddWinningMasternode ( winner ) ) {
2015-04-22 16:33:44 +02:00
winner . Relay ( ) ;
2015-08-05 02:54:02 +02:00
masternodeSync . AddedMasternodeWinner ( winner . GetHash ( ) ) ;
2015-04-16 21:58:09 +02:00
}
}
}
2015-04-22 16:33:44 +02:00
bool CMasternodePaymentWinner : : Sign ( CKey & keyMasternode , CPubKey & pubKeyMasternode )
2015-04-16 21:58:09 +02:00
{
2015-04-22 16:33:44 +02:00
std : : string errorMessage ;
std : : string strMasterNodeSignMessage ;
std : : string strMessage = vinMasternode . prevout . ToStringShort ( ) +
2015-06-25 17:17:53 +02:00
boost : : lexical_cast < std : : string > ( nBlockHeight ) +
2015-04-22 16:33:44 +02:00
payee . ToString ( ) ;
2015-06-25 17:17:53 +02:00
2015-04-22 16:33:44 +02:00
if ( ! darkSendSigner . SignMessage ( strMessage , errorMessage , vchSig , keyMasternode ) ) {
LogPrintf ( " CMasternodePing::Sign() - Error: %s \n " , errorMessage . c_str ( ) ) ;
return false ;
}
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
if ( ! darkSendSigner . VerifyMessage ( pubKeyMasternode , vchSig , strMessage , errorMessage ) ) {
LogPrintf ( " CMasternodePing::Sign() - Error: %s \n " , errorMessage . c_str ( ) ) ;
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 ) ) {
2015-04-22 16:33:44 +02:00
return mapMasternodeBlocks [ nBlockHeight ] . GetPayee ( payee ) ;
}
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
2015-07-22 05:07:23 +02:00
// Is this masternode scheduled to get paid soon?
2015-07-23 15:45:43 +02:00
// -- Only look ahead up to 8 blocks to allow for propagation of the latest 2 winners
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 ) ;
2015-05-27 21:47:01 +02:00
CBlockIndex * pindexPrev = chainActive . Tip ( ) ;
if ( pindexPrev = = NULL ) return false ;
CScript mnpayee ;
mnpayee = GetScriptForDestination ( mn . pubkey . GetID ( ) ) ;
CScript payee ;
2015-07-23 15:45:43 +02:00
for ( int64_t h = pindexPrev - > nHeight ; h < = pindexPrev - > nHeight + 8 ; h + + ) {
2015-06-15 02:05:51 +02:00
if ( h = = nNotBlockHeight ) continue ;
2015-05-27 21:47:01 +02:00
if ( mapMasternodeBlocks . count ( h ) ) {
2015-05-28 19:45:31 +02:00
if ( mapMasternodeBlocks [ h ] . GetPayee ( payee ) ) {
2015-06-23 18:38:28 +02:00
if ( mnpayee = = payee ) {
2015-05-28 19:45:31 +02:00
return true ;
}
}
2015-05-27 21:47:01 +02:00
}
}
return false ;
}
2015-08-30 01:48:19 +02:00
bool CMasternodePayments : : AddWinningMasternode ( CMasternodePaymentWinner & winnerIn )
2015-04-22 16:33:44 +02:00
{
uint256 blockHash = 0 ;
2015-05-26 16:56:51 +02:00
if ( ! GetBlockHash ( blockHash , winnerIn . nBlockHeight - 100 ) ) {
2015-04-16 21:58:09 +02:00
return false ;
}
2015-07-29 17:28:49 +02:00
{
LOCK2 ( cs_mapMasternodePayeeVotes , cs_mapMasternodeBlocks ) ;
if ( mapMasternodePayeeVotes . count ( winnerIn . GetHash ( ) ) ) {
return false ;
}
2015-04-16 21:58:09 +02:00
2015-07-29 17:28:49 +02:00
mapMasternodePayeeVotes [ winnerIn . GetHash ( ) ] = winnerIn ;
2015-04-22 16:33:44 +02:00
2015-07-29 17:28:49 +02:00
if ( ! mapMasternodeBlocks . count ( winnerIn . nBlockHeight ) ) {
CMasternodeBlockPayees blockPayees ( winnerIn . nBlockHeight ) ;
mapMasternodeBlocks [ winnerIn . nBlockHeight ] = blockPayees ;
}
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
int n = 1 ;
if ( IsReferenceNode ( winnerIn . vinMasternode ) ) n = 100 ;
2015-08-30 01:48:19 +02:00
mapMasternodeBlocks [ winnerIn . nBlockHeight ] . AddPayee ( winnerIn . payee , n ) ;
2015-04-22 16:33:44 +02:00
2015-04-16 21:58:09 +02:00
return true ;
}
2015-04-22 16:33:44 +02:00
bool CMasternodeBlockPayees : : IsTransactionValid ( const CTransaction & txNew )
2015-04-16 21:58:09 +02:00
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_vecPayments ) ;
2015-05-04 17:04:09 +02:00
int nMaxSignatures = 0 ;
std : : string strPayeesPossible = " " ;
2015-06-25 20:08:50 +02:00
CAmount masternodePayment = GetMasternodePayment ( nBlockHeight , txNew . GetValueOut ( ) ) ;
2015-05-04 17:04:09 +02:00
//require at least 6 signatures
BOOST_FOREACH ( CMasternodePayee & payee , vecPayments )
2015-05-16 04:53:53 +02:00
if ( payee . nVotes > = nMaxSignatures & & payee . nVotes > = MNPAYMENTS_SIGNATURES_REQUIRED )
2015-05-04 17:04:09 +02:00
nMaxSignatures = payee . nVotes ;
2015-06-25 17:17:53 +02:00
2015-05-04 17:04:09 +02:00
// if we don't have at least 6 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
BOOST_FOREACH ( CMasternodePayee & payee , vecPayments )
2015-04-22 16:33:44 +02:00
{
bool found = false ;
2015-06-25 17:17:53 +02:00
BOOST_FOREACH ( CTxOut out , txNew . vout ) {
2015-06-25 20:08:50 +02:00
if ( payee . scriptPubKey = = out . scriptPubKey & & masternodePayment = = out . nValue ) {
2015-04-22 16:33:44 +02:00
found = true ;
2015-06-25 17:17:53 +02:00
}
}
2015-04-16 21:58:09 +02:00
2015-06-25 17:17:53 +02:00
if ( payee . nVotes > = MNPAYMENTS_SIGNATURES_REQUIRED ) {
2015-05-04 17:04:09 +02:00
if ( found ) return true ;
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
CTxDestination address1 ;
ExtractDestination ( payee . scriptPubKey , address1 ) ;
CBitcoinAddress address2 ( address1 ) ;
2015-04-16 21:58:09 +02:00
2015-05-04 17:04:09 +02:00
if ( strPayeesPossible = = " " ) {
2015-06-25 20:08:50 +02:00
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
LogPrintf ( " CMasternodePayments::IsTransactionValid - Missing required payment - %s \n " , strPayeesPossible . c_str ( ) ) ;
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
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_vecPayments ) ;
2015-04-22 16:33:44 +02:00
std : : string ret = " Unknown " ;
2015-05-04 17:04:09 +02:00
BOOST_FOREACH ( CMasternodePayee & payee , vecPayments )
2015-04-22 16:33:44 +02:00
{
2015-05-26 16:56:51 +02:00
CTxDestination address1 ;
ExtractDestination ( payee . scriptPubKey , address1 ) ;
CBitcoinAddress address2 ( address1 ) ;
2015-04-22 16:33:44 +02:00
2015-05-26 16:56:51 +02:00
if ( ret ! = " Unknown " ) {
2015-06-25 20:08:50 +02:00
ret + = " , " + address2 . ToString ( ) + " : " + boost : : lexical_cast < std : : string > ( payee . nVotes ) ;
2015-05-26 16:56:51 +02:00
} else {
2015-06-25 20:22:11 +02:00
ret = address2 . ToString ( ) + " : " + boost : : lexical_cast < std : : string > ( payee . nVotes ) ;
2015-04-16 21:58:09 +02:00
}
}
2015-05-04 17:04:09 +02:00
2015-04-22 16:33:44 +02:00
return ret ;
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
}
void CMasternodePayments : : CleanPaymentList ( )
{
2015-08-25 12:58:48 +02:00
LOCK2 ( cs_mapMasternodePayeeVotes , cs_mapMasternodeBlocks ) ;
2015-04-16 21:58:09 +02:00
if ( chainActive . Tip ( ) = = NULL ) return ;
2015-07-22 05:07:23 +02:00
//keep up to five cycles for historical sake
2015-09-02 18:53:53 +02:00
int nLimit = std : : max ( int ( mnodeman . size ( ) * 1.25 ) , 1000 ) ;
2015-04-16 21:58:09 +02:00
2015-05-04 17:04:09 +02:00
std : : map < uint256 , CMasternodePaymentWinner > : : iterator it = mapMasternodePayeeVotes . begin ( ) ;
while ( it ! = mapMasternodePayeeVotes . end ( ) ) {
CMasternodePaymentWinner winner = ( * it ) . second ;
2015-06-25 17:17:53 +02:00
2015-05-04 17:04:09 +02:00
if ( chainActive . Tip ( ) - > nHeight - winner . nBlockHeight > nLimit ) {
2015-07-31 17:46:47 +02:00
LogPrint ( " mnpayments " , " CMasternodePayments::CleanPaymentList - Removing old Masternode payment - block %d \n " , winner . nBlockHeight ) ;
2015-08-05 02:54:02 +02:00
masternodeSync . mapSeenSyncMNW . erase ( ( * it ) . first ) ;
2015-05-04 17:04:09 +02:00
mapMasternodePayeeVotes . erase ( it + + ) ;
2015-08-25 12:58:48 +02:00
mapMasternodeBlocks . erase ( winner . 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
}
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
bool IsReferenceNode ( CTxIn & vin )
2015-04-16 21:58:09 +02:00
{
2015-04-22 16:33:44 +02:00
//reference node - hybrid mode
if ( vin . prevout . ToStringShort ( ) = = " 099c01bea63abd1692f60806bb646fa1d288e2d049281225f17e499024084e28-0 " ) return true ; // mainnet
2015-06-19 20:33:02 +02:00
if ( vin . prevout . ToStringShort ( ) = = " fbc16ae5229d6d99181802fd76a4feee5e7640164dcebc7f8feb04a7bea026f8-0 " ) return true ; // testnet
if ( vin . prevout . ToStringShort ( ) = = " e466f5d8beb4c2d22a314310dc58e0ea89505c95409754d0d68fb874952608cc-1 " ) return true ; // regtest
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
2015-08-30 01:48:19 +02:00
bool CMasternodePaymentWinner : : IsValid ( CNode * pnode , std : : string & strError )
2015-04-22 16:33:44 +02:00
{
if ( IsReferenceNode ( vinMasternode ) ) return true ;
2015-04-16 21:58:09 +02:00
2015-07-21 17:09:17 +02:00
CMasternode * pmn = mnodeman . Find ( vinMasternode ) ;
2015-04-16 21:58:09 +02:00
2015-07-21 17:09:17 +02:00
if ( ! pmn )
2015-04-16 21:58:09 +02:00
{
2015-07-21 17:09:17 +02:00
strError = strprintf ( " Unknown Masternode %s " , vinMasternode . prevout . ToStringShort ( ) ) ;
LogPrintf ( " CMasternodePaymentWinner::IsValid - %s \n " , strError ) ;
2015-08-07 05:07:40 +02:00
mnodeman . AskForMN ( pnode , vinMasternode ) ;
2015-04-22 16:33:44 +02:00
return false ;
2015-04-16 21:58:09 +02:00
}
2015-07-22 01:11:49 +02:00
if ( pmn - > protocolVersion < MIN_MNW_PEER_PROTO_VERSION )
2015-07-21 17:09:17 +02:00
{
2015-07-22 01:11:49 +02:00
strError = strprintf ( " Masternode protocol too old %d - req %d " , pmn - > protocolVersion , MIN_MNW_PEER_PROTO_VERSION ) ;
2015-07-21 17:09:17 +02:00
LogPrintf ( " CMasternodePaymentWinner::IsValid - %s \n " , strError ) ;
return false ;
}
2015-08-30 01:48:19 +02:00
int n = mnodeman . GetMasternodeRank ( vinMasternode , nBlockHeight - 100 , MIN_MNW_PEER_PROTO_VERSION ) ;
2015-07-21 17:09:17 +02:00
2015-08-30 01:48:19 +02:00
if ( n > MNPAYMENTS_SIGNATURES_TOTAL )
2015-08-28 17:53:08 +02:00
{
//It's common to have masternodes mistakenly think they are in the top 10
// We don't want to print all of these messages, or punish them unless they're way off
2015-08-30 01:48:19 +02:00
if ( n > MNPAYMENTS_SIGNATURES_TOTAL * 2 )
2015-08-28 17:53:08 +02:00
{
2015-08-30 01:48:19 +02:00
strError = strprintf ( " Masternode not in the top %d (%d) " , MNPAYMENTS_SIGNATURES_TOTAL , n ) ;
2015-08-28 17:53:08 +02:00
LogPrintf ( " CMasternodePaymentWinner::IsValid - %s \n " , strError ) ;
2015-10-02 09:21:49 +02:00
if ( masternodeSync . IsSynced ( ) ) Misbehaving ( pnode - > GetId ( ) , 20 ) ;
2015-08-28 17:53:08 +02:00
}
2015-04-22 16:33:44 +02:00
return false ;
}
return true ;
}
bool CMasternodePayments : : ProcessBlock ( int nBlockHeight )
{
if ( ! fMasterNode ) return false ;
//reference node - hybrid mode
if ( ! IsReferenceNode ( activeMasternode . vin ) ) {
2015-08-30 01:48:19 +02:00
int n = mnodeman . GetMasternodeRank ( activeMasternode . vin , nBlockHeight - 100 , MIN_MNW_PEER_PROTO_VERSION ) ;
2015-04-22 16:33:44 +02:00
2015-08-30 01:48:19 +02:00
if ( n = = - 1 )
2015-04-22 16:33:44 +02:00
{
2015-07-31 17:46:47 +02:00
LogPrint ( " mnpayments " , " CMasternodePayments::ProcessBlock - Unknown Masternode \n " ) ;
2015-04-22 16:33:44 +02:00
return false ;
2015-04-16 21:58:09 +02:00
}
2015-08-30 01:48:19 +02:00
if ( n > MNPAYMENTS_SIGNATURES_TOTAL )
2015-04-22 16:33:44 +02:00
{
2015-08-30 01:48:19 +02:00
LogPrint ( " mnpayments " , " CMasternodePayments::ProcessBlock - Masternode not in the top %d (%d) \n " , MNPAYMENTS_SIGNATURES_TOTAL , n ) ;
2015-04-22 16:33:44 +02:00
return false ;
}
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
if ( nBlockHeight < = nLastBlockHeight ) return false ;
2015-06-25 17:17:53 +02:00
2015-05-04 17:04:09 +02:00
CMasternodePaymentWinner newWinner ( activeMasternode . vin ) ;
2015-04-16 21:58:09 +02:00
2015-05-04 17:04:09 +02:00
if ( budget . IsBudgetPaymentBlock ( nBlockHeight ) ) {
2015-05-27 21:47:01 +02:00
//is budget payment block -- handled by the budgeting software
2015-05-04 17:04:09 +02:00
} else {
2015-06-24 18:41:03 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock() Start nHeight %d - vin %s. \n " , nBlockHeight , activeMasternode . vin . ToString ( ) . c_str ( ) ) ;
2015-04-22 16:33:44 +02:00
// pay to the oldest MN that still had no payment but its input is old enough and it was active long enough
2015-08-20 17:36:44 +02:00
int nCount = 0 ;
CMasternode * pmn = mnodeman . GetNextMasternodeInQueueForPayment ( nBlockHeight , true , nCount ) ;
2015-07-25 01:10:44 +02:00
2015-04-22 16:33:44 +02:00
if ( pmn ! = NULL )
{
2015-06-22 15:50:33 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock() Found by FindOldestNotInVec \n " ) ;
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
newWinner . nBlockHeight = nBlockHeight ;
2015-04-16 21:58:09 +02:00
2015-06-23 18:38:28 +02:00
CScript payee = GetScriptForDestination ( pmn - > pubkey . GetID ( ) ) ;
2015-06-25 20:08:50 +02:00
newWinner . AddPayee ( payee ) ;
2015-06-25 17:17:53 +02:00
2015-05-04 17:04:09 +02:00
CTxDestination address1 ;
ExtractDestination ( payee , address1 ) ;
CBitcoinAddress address2 ( address1 ) ;
2015-04-22 16:33:44 +02:00
2015-06-23 18:38:28 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock() Winner payee %s nHeight %d. \n " , address2 . ToString ( ) . c_str ( ) , newWinner . nBlockHeight ) ;
2015-05-04 17:04:09 +02:00
} else {
2015-06-22 15:50:33 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock() Failed to find masternode to pay \n " ) ;
2015-05-04 17:04:09 +02:00
}
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
}
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
std : : string errorMessage ;
CPubKey pubKeyMasternode ;
CKey keyMasternode ;
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
if ( ! darkSendSigner . SetKey ( strMasterNodePrivKey , errorMessage , keyMasternode , pubKeyMasternode ) )
{
LogPrintf ( " CMasternodePayments::ProcessBlock() - Error upon calling SetKey: %s \n " , errorMessage . c_str ( ) ) ;
return false ;
}
2015-04-16 21:58:09 +02:00
2015-06-22 15:50:33 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock() - Signing Winner \n " ) ;
2015-05-04 17:04:09 +02:00
if ( newWinner . Sign ( keyMasternode , pubKeyMasternode ) )
2015-04-16 21:58:09 +02:00
{
2015-06-22 15:50:33 +02:00
LogPrintf ( " CMasternodePayments::ProcessBlock() - AddWinningMasternode \n " ) ;
2015-08-30 01:48:19 +02:00
if ( AddWinningMasternode ( newWinner ) )
2015-04-16 21:58:09 +02:00
{
2015-04-22 16:33:44 +02:00
newWinner . Relay ( ) ;
2015-04-16 21:58:09 +02:00
nLastBlockHeight = nBlockHeight ;
return true ;
}
}
2015-05-04 17:04:09 +02:00
2015-04-16 21:58:09 +02:00
return false ;
}
2015-04-22 16:33:44 +02:00
void CMasternodePaymentWinner : : Relay ( )
2015-04-16 21:58:09 +02:00
{
2015-04-22 16:33:44 +02:00
CInv inv ( MSG_MASTERNODE_WINNER , GetHash ( ) ) ;
2015-07-08 02:37:23 +02:00
RelayInv ( inv ) ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
bool CMasternodePaymentWinner : : SignatureValid ( )
2015-04-16 21:58:09 +02:00
{
2015-04-22 16:33:44 +02:00
CMasternode * pmn = mnodeman . Find ( vinMasternode ) ;
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
if ( pmn ! = NULL )
{
std : : string strMessage = vinMasternode . prevout . ToStringShort ( ) +
2015-06-25 17:17:53 +02:00
boost : : lexical_cast < std : : string > ( nBlockHeight ) +
2015-04-22 16:33:44 +02:00
payee . ToString ( ) ;
2015-04-16 21:58:09 +02:00
2015-04-22 16:33:44 +02:00
std : : string errorMessage = " " ;
if ( ! darkSendSigner . VerifyMessage ( pmn - > pubkey2 , vchSig , strMessage , errorMessage ) ) {
return error ( " CMasternodePaymentWinner::SignatureValid() - Got bad Masternode address signature % s \ n " , vinMasternode.ToString().c_str()) ;
}
2015-04-16 21:58:09 +02:00
return true ;
}
2015-04-22 16:33:44 +02:00
return false ;
2015-04-16 21:58:09 +02:00
}
2015-04-22 16:33:44 +02:00
2015-07-21 00:09:42 +02:00
void CMasternodePayments : : Sync ( CNode * node , int nCountNeeded )
2015-04-22 16:33:44 +02:00
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_mapMasternodePayeeVotes ) ;
2015-05-04 17:04:09 +02:00
if ( chainActive . Tip ( ) = = NULL ) return ;
2015-09-04 14:22:17 +02:00
int nCount = ( mnodeman . CountEnabled ( ) * 1.25 ) ;
2015-07-21 00:09:42 +02:00
if ( nCountNeeded > nCount ) nCountNeeded = nCount ;
2015-08-28 23:37:53 +02:00
2015-08-31 06:05:10 +02:00
int nInvCount = 0 ;
2015-05-04 17:04:09 +02:00
std : : map < uint256 , CMasternodePaymentWinner > : : iterator it = mapMasternodePayeeVotes . begin ( ) ;
while ( it ! = mapMasternodePayeeVotes . end ( ) ) {
2015-08-30 01:48:19 +02:00
CMasternodePaymentWinner winner = ( * it ) . second ;
if ( winner . nBlockHeight > = chainActive . Tip ( ) - > nHeight - nCountNeeded & & winner . nBlockHeight < = chainActive . Tip ( ) - > nHeight + 20 ) {
2015-08-31 06:05:10 +02:00
node - > PushInventory ( CInv ( MSG_MASTERNODE_WINNER , winner . GetHash ( ) ) ) ;
nInvCount + + ;
2015-07-23 23:35:14 +02:00
}
2015-05-04 17:04:09 +02:00
+ + it ;
}
2015-08-31 06:05:10 +02:00
node - > PushMessage ( " ssc " , MASTERNODE_SYNC_MNW , nInvCount ) ;
2015-05-16 04:53:53 +02:00
}
2015-07-21 04:24:43 +02:00
std : : string CMasternodePayments : : ToString ( ) const
{
std : : ostringstream info ;
info < < " Votes: " < < ( int ) mapMasternodePayeeVotes . size ( ) < <
" , Blocks: " < < ( int ) mapMasternodeBlocks . size ( ) ;
return info . str ( ) ;
}
2015-07-22 00:14:54 +02:00
int CMasternodePayments : : GetOldestBlock ( )
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_mapMasternodeBlocks ) ;
2015-07-22 00:14:54 +02:00
int nOldestBlock = std : : numeric_limits < int > : : max ( ) ;
std : : map < int , CMasternodeBlockPayees > : : iterator it = mapMasternodeBlocks . begin ( ) ;
while ( it ! = mapMasternodeBlocks . end ( ) ) {
if ( ( * it ) . first < nOldestBlock ) {
nOldestBlock = ( * it ) . first ;
}
it + + ;
}
return nOldestBlock ;
}
2015-07-21 04:24:43 +02:00
int CMasternodePayments : : GetNewestBlock ( )
{
2015-07-29 17:28:49 +02:00
LOCK ( cs_mapMasternodeBlocks ) ;
2015-07-21 04:24:43 +02:00
int nNewestBlock = 0 ;
std : : map < int , CMasternodeBlockPayees > : : iterator it = mapMasternodeBlocks . begin ( ) ;
while ( it ! = mapMasternodeBlocks . end ( ) ) {
if ( ( * it ) . first > nNewestBlock ) {
nNewestBlock = ( * it ) . first ;
}
it + + ;
}
return nNewestBlock ;
}