// 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 #include /** 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 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"); } //are these blocks even enabled if(!IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)){ return block.vtx[0].GetValueOut() <= nExpectedValue; } 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 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(IsInitialBlockDownload()) return; 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(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(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(), 100); 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(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(payee.nVotes); } else { ret = address2.ToString() + ":" + boost::lexical_cast(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())*5, 1000); std::map::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(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); 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, GetMinMasternodePaymentsProto()); 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 CMasternode *pmn = mnodeman.GetNextMasternodeInQueueForPayment(nBlockHeight, true); if(pmn == NULL) pmn = mnodeman.GetNextMasternodeInQueueForPayment(nBlockHeight, false); // if no results, look for any node with a newer sigTime 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(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 vInv; std::map::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::max(); std::map::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::iterator it = mapMasternodeBlocks.begin(); while(it != mapMasternodeBlocks.end()) { if((*it).first > nNewestBlock) { nNewestBlock = (*it).first; } it++; } return nNewestBlock; }