402d821529
- mapSeenMasternodePing & mapSeenMasternodeBroadcast were not getting cleaned, so the pinging of 3000 masternode was accumulating over time. This patch has the clean clean the lists every few minutes and removes anything older than 20 minutes for pings and 2.5 hours for broadcasts. - v48
839 lines
27 KiB
C++
839 lines
27 KiB
C++
// 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"
|
|
#include "masternode-budget.h"
|
|
#include "masternode-sync.h"
|
|
#include "masternodeman.h"
|
|
#include "darksend.h"
|
|
#include "util.h"
|
|
#include "sync.h"
|
|
#include "spork.h"
|
|
#include "addrman.h"
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
|
|
/** Object for who's going to get paid on which blocks */
|
|
CMasternodePayments masternodePayments;
|
|
|
|
CCriticalSection cs_vecPayments;
|
|
CCriticalSection cs_mapMasternodeBlocks;
|
|
CCriticalSection cs_mapMasternodePayeeVotes;
|
|
|
|
//
|
|
// 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) {
|
|
LogPrintf("Masternode payments manager - cleaning....\n");
|
|
objToLoad.CleanPaymentList();
|
|
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);
|
|
}
|
|
|
|
bool IsBlockValueValid(const CBlock& block, int64_t nExpectedValue){
|
|
CBlockIndex* pindexPrev = chainActive.Tip();
|
|
if(pindexPrev == NULL) return true;
|
|
|
|
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");
|
|
}
|
|
|
|
if(!masternodeSync.IsSynced()) { //there is no budget data to use to check anything
|
|
//super blocks will always be on these blocks, max 100 per budgeting
|
|
if(nHeight % GetBudgetPaymentCycleBlocks() < 100){
|
|
return true;
|
|
} else {
|
|
if(block.vtx[0].GetValueOut() > nExpectedValue) return false;
|
|
}
|
|
} else { // we're synced and have data so check the budget schedule
|
|
|
|
//are these blocks even enabled
|
|
if(!IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)){
|
|
return block.vtx[0].GetValueOut() <= nExpectedValue;
|
|
}
|
|
|
|
if(budget.IsBudgetPaymentBlock(nHeight)){
|
|
//the value of the block is evaluated in CheckBlock
|
|
return true;
|
|
} else {
|
|
if(block.vtx[0].GetValueOut() > nExpectedValue) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight)
|
|
{
|
|
if(!masternodeSync.IsSynced()) { //there is no budget data to use to check anything -- find the longest chain
|
|
LogPrint("mnpayments", "Client not synced, skipping block payee checks\n");
|
|
return true;
|
|
}
|
|
|
|
//check if it's a budget block
|
|
if(IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)){
|
|
if(budget.IsBudgetPaymentBlock(nBlockHeight)){
|
|
if(budget.IsTransactionValid(txNew, nBlockHeight)){
|
|
return true;
|
|
} 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//check for masternode payee
|
|
if(masternodePayments.IsTransactionValid(txNew, nBlockHeight))
|
|
{
|
|
return true;
|
|
} else {
|
|
LogPrintf("Invalid mn payment detected %s\n", txNew.ToString().c_str());
|
|
if(IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)){
|
|
return false;
|
|
} else {
|
|
LogPrintf("Masternode payment enforcement is disabled, accepting block\n");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void FillBlockPayee(CMutableTransaction& txNew, int64_t nFees)
|
|
{
|
|
CBlockIndex* pindexPrev = chainActive.Tip();
|
|
if(!pindexPrev) return;
|
|
|
|
if(IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && budget.IsBudgetPaymentBlock(pindexPrev->nHeight+1)){
|
|
budget.FillBlockPayee(txNew, nFees);
|
|
} else {
|
|
masternodePayments.FillBlockPayee(txNew, nFees);
|
|
}
|
|
}
|
|
|
|
std::string GetRequiredPaymentsString(int nBlockHeight)
|
|
{
|
|
if(IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && budget.IsBudgetPaymentBlock(nBlockHeight)){
|
|
return budget.GetRequiredPaymentsString(nBlockHeight);
|
|
} else {
|
|
return masternodePayments.GetRequiredPaymentsString(nBlockHeight);
|
|
}
|
|
}
|
|
|
|
void CMasternodePayments::FillBlockPayee(CMutableTransaction& txNew, int64_t nFees)
|
|
{
|
|
CBlockIndex* pindexPrev = chainActive.Tip();
|
|
if(!pindexPrev) return;
|
|
|
|
bool hasPayment = true;
|
|
CScript payee;
|
|
|
|
//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);
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
int CMasternodePayments::GetMinMasternodePaymentsProto() {
|
|
return IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)
|
|
? MIN_MASTERNODE_PAYMENT_PROTO_VERSION_2
|
|
: MIN_MASTERNODE_PAYMENT_PROTO_VERSION_1;
|
|
}
|
|
|
|
void CMasternodePayments::ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
|
|
{
|
|
if(!masternodeSync.IsBlockchainSynced()) return;
|
|
|
|
if(fLiteMode) return; //disable all Darksend/Masternode related functionality
|
|
|
|
|
|
if (strCommand == "mnget") { //Masternode Payments Request Sync
|
|
if(fLiteMode) return; //disable all Darksend/Masternode related functionality
|
|
|
|
int nCountNeeded;
|
|
vRecv >> nCountNeeded;
|
|
|
|
if(Params().NetworkID() == CBaseChainParams::MAIN){
|
|
if(pfrom->HasFulfilledRequest("mnget")) {
|
|
LogPrintf("mnget - peer already asked me for the list\n");
|
|
Misbehaving(pfrom->GetId(), 20);
|
|
return;
|
|
}
|
|
}
|
|
|
|
pfrom->FulfilledRequest("mnget");
|
|
masternodePayments.Sync(pfrom, nCountNeeded);
|
|
LogPrintf("mnget - Sent Masternode winners to %s\n", pfrom->addr.ToString().c_str());
|
|
}
|
|
else if (strCommand == "mnw") { //Masternode Payments Declare Winner
|
|
//this is required in litemodef
|
|
CMasternodePaymentWinner winner;
|
|
vRecv >> winner;
|
|
|
|
if(pfrom->nVersion < MIN_MNW_PEER_PROTO_VERSION) return;
|
|
|
|
if(chainActive.Tip() == NULL) return;
|
|
|
|
if(masternodePayments.mapMasternodePayeeVotes.count(winner.GetHash())){
|
|
LogPrint("mnpayments", "mnw - Already seen - %s bestHeight %d\n", winner.GetHash().ToString().c_str(), chainActive.Tip()->nHeight);
|
|
masternodeSync.AddedMasternodeWinner(winner.GetHash());
|
|
return;
|
|
}
|
|
|
|
int nFirstBlock = chainActive.Tip()->nHeight - (mnodeman.CountEnabled()*2);
|
|
if(winner.nBlockHeight < nFirstBlock || winner.nBlockHeight > chainActive.Tip()->nHeight+20){
|
|
LogPrintf("mnw - winner out of range - FirstBlock %d Height %d bestHeight %d\n", nFirstBlock, winner.nBlockHeight, chainActive.Tip()->nHeight);
|
|
return;
|
|
}
|
|
|
|
std::string strError = "";
|
|
if(!winner.IsValid(pfrom, strError)){
|
|
LogPrintf("mnw - invalid message - %s\n", strError);
|
|
return;
|
|
}
|
|
|
|
if(!masternodePayments.CanVote(winner.vinMasternode.prevout, winner.nBlockHeight)){
|
|
LogPrintf("mnw - masternode already voted - %s\n", winner.vinMasternode.prevout.ToStringShort());
|
|
return;
|
|
}
|
|
|
|
if(!winner.SignatureValid()){
|
|
LogPrintf("mnw - invalid signature\n");
|
|
Misbehaving(pfrom->GetId(), 20);
|
|
// it could just be a non-synced masternode
|
|
mnodeman.AskForMN(pfrom, winner.vinMasternode);
|
|
return;
|
|
}
|
|
|
|
CTxDestination address1;
|
|
ExtractDestination(winner.payee, address1);
|
|
CBitcoinAddress address2(address1);
|
|
|
|
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());
|
|
|
|
if(masternodePayments.AddWinningMasternode(winner)){
|
|
winner.Relay();
|
|
masternodeSync.AddedMasternodeWinner(winner.GetHash());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CMasternodePaymentWinner::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
|
|
{
|
|
std::string errorMessage;
|
|
std::string strMasterNodeSignMessage;
|
|
|
|
std::string strMessage = vinMasternode.prevout.ToStringShort() +
|
|
boost::lexical_cast<std::string>(nBlockHeight) +
|
|
payee.ToString();
|
|
|
|
if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) {
|
|
LogPrintf("CMasternodePing::Sign() - Error: %s\n", errorMessage.c_str());
|
|
return false;
|
|
}
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) {
|
|
LogPrintf("CMasternodePing::Sign() - Error: %s\n", errorMessage.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePayments::GetBlockPayee(int nBlockHeight, CScript& payee)
|
|
{
|
|
if(mapMasternodeBlocks.count(nBlockHeight)){
|
|
return mapMasternodeBlocks[nBlockHeight].GetPayee(payee);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Is this masternode scheduled to get paid soon?
|
|
// -- Only look ahead up to 8 blocks to allow for propagation of the latest 2 winners
|
|
bool CMasternodePayments::IsScheduled(CMasternode& mn, int nNotBlockHeight)
|
|
{
|
|
LOCK(cs_mapMasternodeBlocks);
|
|
|
|
CBlockIndex* pindexPrev = chainActive.Tip();
|
|
if(pindexPrev == NULL) return false;
|
|
|
|
CScript mnpayee;
|
|
mnpayee = GetScriptForDestination(mn.pubkey.GetID());
|
|
|
|
CScript payee;
|
|
for(int64_t h = pindexPrev->nHeight; h <= pindexPrev->nHeight+8; h++){
|
|
if(h == nNotBlockHeight) continue;
|
|
if(mapMasternodeBlocks.count(h)){
|
|
if(mapMasternodeBlocks[h].GetPayee(payee)){
|
|
if(mnpayee == payee) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CMasternodePayments::AddWinningMasternode(CMasternodePaymentWinner& winnerIn)
|
|
{
|
|
uint256 blockHash = 0;
|
|
if(!GetBlockHash(blockHash, winnerIn.nBlockHeight-100)) {
|
|
return false;
|
|
}
|
|
|
|
{
|
|
LOCK2(cs_mapMasternodePayeeVotes, cs_mapMasternodeBlocks);
|
|
|
|
if(mapMasternodePayeeVotes.count(winnerIn.GetHash())){
|
|
return false;
|
|
}
|
|
|
|
mapMasternodePayeeVotes[winnerIn.GetHash()] = winnerIn;
|
|
|
|
if(!mapMasternodeBlocks.count(winnerIn.nBlockHeight)){
|
|
CMasternodeBlockPayees blockPayees(winnerIn.nBlockHeight);
|
|
mapMasternodeBlocks[winnerIn.nBlockHeight] = blockPayees;
|
|
}
|
|
}
|
|
|
|
int n = 1;
|
|
if(IsReferenceNode(winnerIn.vinMasternode)) n = 100;
|
|
mapMasternodeBlocks[winnerIn.nBlockHeight].AddPayee(winnerIn.payee, n);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew)
|
|
{
|
|
LOCK(cs_vecPayments);
|
|
|
|
int nMaxSignatures = 0;
|
|
std::string strPayeesPossible = "";
|
|
|
|
CAmount masternodePayment = GetMasternodePayment(nBlockHeight, txNew.GetValueOut());
|
|
|
|
//require at least 6 signatures
|
|
|
|
BOOST_FOREACH(CMasternodePayee& payee, vecPayments)
|
|
if(payee.nVotes >= nMaxSignatures && payee.nVotes >= MNPAYMENTS_SIGNATURES_REQUIRED)
|
|
nMaxSignatures = payee.nVotes;
|
|
|
|
// if we don't have at least 6 signatures on a payee, approve whichever is the longest chain
|
|
if(nMaxSignatures < MNPAYMENTS_SIGNATURES_REQUIRED) return true;
|
|
|
|
BOOST_FOREACH(CMasternodePayee& payee, vecPayments)
|
|
{
|
|
bool found = false;
|
|
BOOST_FOREACH(CTxOut out, txNew.vout){
|
|
if(payee.scriptPubKey == out.scriptPubKey && masternodePayment == out.nValue){
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if(payee.nVotes >= MNPAYMENTS_SIGNATURES_REQUIRED){
|
|
if(found) return true;
|
|
|
|
CTxDestination address1;
|
|
ExtractDestination(payee.scriptPubKey, address1);
|
|
CBitcoinAddress address2(address1);
|
|
|
|
if(strPayeesPossible == ""){
|
|
strPayeesPossible += address2.ToString();
|
|
} else {
|
|
strPayeesPossible += "," + address2.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LogPrintf("CMasternodePayments::IsTransactionValid - Missing required payment - %s\n", strPayeesPossible.c_str());
|
|
return false;
|
|
}
|
|
|
|
std::string CMasternodeBlockPayees::GetRequiredPaymentsString()
|
|
{
|
|
LOCK(cs_vecPayments);
|
|
|
|
std::string ret = "Unknown";
|
|
|
|
BOOST_FOREACH(CMasternodePayee& payee, vecPayments)
|
|
{
|
|
CTxDestination address1;
|
|
ExtractDestination(payee.scriptPubKey, address1);
|
|
CBitcoinAddress address2(address1);
|
|
|
|
if(ret != "Unknown"){
|
|
ret += ", " + address2.ToString() + ":" + boost::lexical_cast<std::string>(payee.nVotes);
|
|
} else {
|
|
ret = address2.ToString() + ":" + boost::lexical_cast<std::string>(payee.nVotes);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string CMasternodePayments::GetRequiredPaymentsString(int nBlockHeight)
|
|
{
|
|
LOCK(cs_mapMasternodeBlocks);
|
|
|
|
if(mapMasternodeBlocks.count(nBlockHeight)){
|
|
return mapMasternodeBlocks[nBlockHeight].GetRequiredPaymentsString();
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlockHeight)
|
|
{
|
|
LOCK(cs_mapMasternodeBlocks);
|
|
|
|
if(mapMasternodeBlocks.count(nBlockHeight)){
|
|
return mapMasternodeBlocks[nBlockHeight].IsTransactionValid(txNew);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMasternodePayments::CleanPaymentList()
|
|
{
|
|
LOCK(cs_mapMasternodePayeeVotes);
|
|
|
|
if(chainActive.Tip() == NULL) return;
|
|
|
|
//keep up to five cycles for historical sake
|
|
int nLimit = std::max(((int)mnodeman.size())*2, 1000);
|
|
|
|
std::map<uint256, CMasternodePaymentWinner>::iterator it = mapMasternodePayeeVotes.begin();
|
|
while(it != mapMasternodePayeeVotes.end()) {
|
|
CMasternodePaymentWinner winner = (*it).second;
|
|
|
|
if(chainActive.Tip()->nHeight - winner.nBlockHeight > nLimit){
|
|
LogPrint("mnpayments", "CMasternodePayments::CleanPaymentList - Removing old Masternode payment - block %d\n", winner.nBlockHeight);
|
|
masternodeSync.mapSeenSyncMNW.erase((*it).first);
|
|
mapMasternodePayeeVotes.erase(it++);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsReferenceNode(CTxIn& vin)
|
|
{
|
|
//reference node - hybrid mode
|
|
if(vin.prevout.ToStringShort() == "099c01bea63abd1692f60806bb646fa1d288e2d049281225f17e499024084e28-0") return true; // mainnet
|
|
if(vin.prevout.ToStringShort() == "fbc16ae5229d6d99181802fd76a4feee5e7640164dcebc7f8feb04a7bea026f8-0") return true; // testnet
|
|
if(vin.prevout.ToStringShort() == "e466f5d8beb4c2d22a314310dc58e0ea89505c95409754d0d68fb874952608cc-1") return true; // regtest
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CMasternodePaymentWinner::IsValid(CNode* pnode, std::string& strError)
|
|
{
|
|
if(IsReferenceNode(vinMasternode)) return true;
|
|
|
|
CMasternode* pmn = mnodeman.Find(vinMasternode);
|
|
|
|
if(!pmn)
|
|
{
|
|
strError = strprintf("Unknown Masternode %s", vinMasternode.prevout.ToStringShort());
|
|
LogPrintf ("CMasternodePaymentWinner::IsValid - %s\n", strError);
|
|
mnodeman.AskForMN(pnode, vinMasternode);
|
|
return false;
|
|
}
|
|
|
|
if(pmn->protocolVersion < MIN_MNW_PEER_PROTO_VERSION)
|
|
{
|
|
strError = strprintf("Masternode protocol too old %d - req %d", pmn->protocolVersion, MIN_MNW_PEER_PROTO_VERSION);
|
|
LogPrintf ("CMasternodePaymentWinner::IsValid - %s\n", strError);
|
|
return false;
|
|
}
|
|
|
|
int n = mnodeman.GetMasternodeRank(vinMasternode, nBlockHeight-100, MIN_MNW_PEER_PROTO_VERSION);
|
|
|
|
if(n > MNPAYMENTS_SIGNATURES_TOTAL)
|
|
{
|
|
strError = strprintf("Masternode not in the top %d (%d)", MNPAYMENTS_SIGNATURES_TOTAL, n);
|
|
LogPrintf("CMasternodePaymentWinner::IsValid - %s\n", strError);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePayments::ProcessBlock(int nBlockHeight)
|
|
{
|
|
if(!fMasterNode) return false;
|
|
|
|
//reference node - hybrid mode
|
|
|
|
if(!IsReferenceNode(activeMasternode.vin)){
|
|
int n = mnodeman.GetMasternodeRank(activeMasternode.vin, nBlockHeight-100, MIN_MNW_PEER_PROTO_VERSION);
|
|
|
|
if(n == -1)
|
|
{
|
|
LogPrint("mnpayments", "CMasternodePayments::ProcessBlock - Unknown Masternode\n");
|
|
return false;
|
|
}
|
|
|
|
if(n > MNPAYMENTS_SIGNATURES_TOTAL)
|
|
{
|
|
LogPrint("mnpayments", "CMasternodePayments::ProcessBlock - Masternode not in the top %d (%d)\n", MNPAYMENTS_SIGNATURES_TOTAL, n);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(nBlockHeight <= nLastBlockHeight) return false;
|
|
|
|
CMasternodePaymentWinner newWinner(activeMasternode.vin);
|
|
|
|
if(budget.IsBudgetPaymentBlock(nBlockHeight)){
|
|
//is budget payment block -- handled by the budgeting software
|
|
} else {
|
|
LogPrintf("CMasternodePayments::ProcessBlock() Start nHeight %d - vin %s. \n", nBlockHeight, activeMasternode.vin.ToString().c_str());
|
|
|
|
// 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);
|
|
|
|
if(pmn != NULL)
|
|
{
|
|
LogPrintf("CMasternodePayments::ProcessBlock() Found by FindOldestNotInVec \n");
|
|
|
|
newWinner.nBlockHeight = nBlockHeight;
|
|
|
|
CScript payee = GetScriptForDestination(pmn->pubkey.GetID());
|
|
newWinner.AddPayee(payee);
|
|
|
|
CTxDestination address1;
|
|
ExtractDestination(payee, address1);
|
|
CBitcoinAddress address2(address1);
|
|
|
|
LogPrintf("CMasternodePayments::ProcessBlock() Winner payee %s nHeight %d. \n", address2.ToString().c_str(), newWinner.nBlockHeight);
|
|
} else {
|
|
LogPrintf("CMasternodePayments::ProcessBlock() Failed to find masternode to pay\n");
|
|
}
|
|
|
|
}
|
|
|
|
std::string errorMessage;
|
|
CPubKey pubKeyMasternode;
|
|
CKey keyMasternode;
|
|
|
|
if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode))
|
|
{
|
|
LogPrintf("CMasternodePayments::ProcessBlock() - Error upon calling SetKey: %s\n", errorMessage.c_str());
|
|
return false;
|
|
}
|
|
|
|
LogPrintf("CMasternodePayments::ProcessBlock() - Signing Winner\n");
|
|
if(newWinner.Sign(keyMasternode, pubKeyMasternode))
|
|
{
|
|
LogPrintf("CMasternodePayments::ProcessBlock() - AddWinningMasternode\n");
|
|
|
|
if(AddWinningMasternode(newWinner))
|
|
{
|
|
newWinner.Relay();
|
|
nLastBlockHeight = nBlockHeight;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CMasternodePaymentWinner::Relay()
|
|
{
|
|
CInv inv(MSG_MASTERNODE_WINNER, GetHash());
|
|
RelayInv(inv);
|
|
}
|
|
|
|
bool CMasternodePaymentWinner::SignatureValid()
|
|
{
|
|
|
|
CMasternode* pmn = mnodeman.Find(vinMasternode);
|
|
|
|
if(pmn != NULL)
|
|
{
|
|
std::string strMessage = vinMasternode.prevout.ToStringShort() +
|
|
boost::lexical_cast<std::string>(nBlockHeight) +
|
|
payee.ToString();
|
|
|
|
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());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CMasternodePayments::Sync(CNode* node, int nCountNeeded)
|
|
{
|
|
LOCK(cs_mapMasternodePayeeVotes);
|
|
|
|
if(chainActive.Tip() == NULL) return;
|
|
|
|
int nCount = (mnodeman.CountEnabled()*2);
|
|
if(nCountNeeded > nCount) nCountNeeded = nCount;
|
|
|
|
vector<CInv> vInv;
|
|
std::map<uint256, CMasternodePaymentWinner>::iterator it = mapMasternodePayeeVotes.begin();
|
|
while(it != mapMasternodePayeeVotes.end()) {
|
|
CMasternodePaymentWinner winner = (*it).second;
|
|
if(winner.nBlockHeight >= chainActive.Tip()->nHeight-nCountNeeded && winner.nBlockHeight <= chainActive.Tip()->nHeight + 20) {
|
|
CInv inv(MSG_MASTERNODE_WINNER, winner.GetHash());
|
|
vInv.push_back(inv);
|
|
}
|
|
++it;
|
|
}
|
|
node->PushMessage("ssc", MASTERNODE_SYNC_MNW, (int)vInv.size());
|
|
if(vInv.size() > 0) node->PushMessage("inv", vInv);
|
|
}
|
|
|
|
std::string CMasternodePayments::ToString() const
|
|
{
|
|
std::ostringstream info;
|
|
|
|
info << "Votes: " << (int)mapMasternodePayeeVotes.size() <<
|
|
", Blocks: " << (int)mapMasternodeBlocks.size();
|
|
|
|
return info.str();
|
|
}
|
|
|
|
|
|
|
|
int CMasternodePayments::GetOldestBlock()
|
|
{
|
|
LOCK(cs_mapMasternodeBlocks);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
int CMasternodePayments::GetNewestBlock()
|
|
{
|
|
LOCK(cs_mapMasternodeBlocks);
|
|
|
|
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;
|
|
}
|