// Copyright (c) 2014-2016 The Dash Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "masternodeman.h" #include "activemasternode.h" #include "darksend.h" #include "masternode.h" #include "masternode-payments.h" #include "masternode-sync.h" #include "netfulfilledman.h" #include "util.h" #include "addrman.h" #include "spork.h" #include #include /** Masternode manager */ CMasternodeMan mnodeman; const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-1"; struct CompareLastPaidBlock { bool operator()(const std::pair& t1, const std::pair& t2) const { return (t1.first != t2.first) ? (t1.first < t2.first) : (t1.second->vin < t2.second->vin); } }; struct CompareScoreMN { bool operator()(const std::pair& t1, const std::pair& t2) const { return (t1.first != t2.first) ? (t1.first < t2.first) : (t1.second->vin < t2.second->vin); } }; struct CompareByAddr { bool operator()(const CMasternode* t1, const CMasternode* t2) const { return t1->addr < t2->addr; } }; bool CMasternodeMan::Add(CMasternode &mn) { LOCK(cs); if (!mn.IsEnabled() && !mn.IsPreEnabled()) return false; CMasternode *pmn = Find(mn.vin); if (pmn == NULL) { LogPrint("masternode", "CMasternodeMan::Add -- Adding new Masternode: addr=%s, %i now\n", mn.addr.ToString(), size() + 1); mn.nTimeLastWatchdogVote = mn.sigTime; vMasternodes.push_back(mn); return true; } return false; } void CMasternodeMan::AskForMN(CNode* pnode, CTxIn &vin) { if(!pnode) return; std::map::iterator it = mWeAskedForMasternodeListEntry.find(vin.prevout); if (it != mWeAskedForMasternodeListEntry.end() && GetTime() < (*it).second) { // we've asked recently, should not repeat too often or we could get banned return; } // ask for the mnb info once from the node that sent mnp LogPrintf("CMasternodeMan::AskForMN -- Asking node for missing masternode entry: %s\n", vin.prevout.ToStringShort()); pnode->PushMessage(NetMsgType::DSEG, vin); mWeAskedForMasternodeListEntry[vin.prevout] = GetTime() + DSEG_UPDATE_SECONDS;; } void CMasternodeMan::Check() { LOCK(cs); LogPrint("masternode", "CMasternodeMan::Check nLastWatchdogVoteTime = %d, IsWatchdogActive() = %d\n", nLastWatchdogVoteTime, IsWatchdogActive()); BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); } } void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval) { LogPrintf("CMasternodeMan::CheckAndRemove\n"); Check(); LOCK(cs); // Remove inactive and outdated masternodes std::vector::iterator it = vMasternodes.begin(); while(it != vMasternodes.end()) { bool fRemove = // If it's marked to be removed from the list by CMasternode::Check for whatever reason ... (*it).nActiveState == CMasternode::MASTERNODE_REMOVE || // or collateral was spent ... (*it).nActiveState == CMasternode::MASTERNODE_OUTPOINT_SPENT || // or we were asked to remove exired entries ... (fForceExpiredRemoval && (*it).nActiveState == CMasternode::MASTERNODE_EXPIRED); if (fRemove) { LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing Masternode: %s addr=%s %i now\n", (*it).GetStatus(), (*it).addr.ToString(), size() - 1); // erase all of the broadcasts we've seen from this txin, ... mapSeenMasternodeBroadcast.erase(CMasternodeBroadcast(*it).GetHash()); // allow us to ask for this masternode again if we see another ping ... mWeAskedForMasternodeListEntry.erase((*it).vin.prevout); // and finally remove it from the list it = vMasternodes.erase(it); } else { ++it; } } // check who's asked for the Masternode list std::map::iterator it1 = mAskedUsForMasternodeList.begin(); while(it1 != mAskedUsForMasternodeList.end()){ if((*it1).second < GetTime()) { mAskedUsForMasternodeList.erase(it1++); } else { ++it1; } } // check who we asked for the Masternode list it1 = mWeAskedForMasternodeList.begin(); while(it1 != mWeAskedForMasternodeList.end()){ if((*it1).second < GetTime()){ mWeAskedForMasternodeList.erase(it1++); } else { ++it1; } } // check which Masternodes we've asked for std::map::iterator it2 = mWeAskedForMasternodeListEntry.begin(); while(it2 != mWeAskedForMasternodeListEntry.end()){ if((*it2).second < GetTime()){ mWeAskedForMasternodeListEntry.erase(it2++); } else { ++it2; } } std::map::iterator itv1 = mWeAskedForVerification.begin(); while(itv1 != mWeAskedForVerification.end()){ if(itv1->second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) { mWeAskedForVerification.erase(itv1++); } else { ++itv1; } } // remove expired mapSeenMasternodeBroadcast std::map::iterator it3 = mapSeenMasternodeBroadcast.begin(); while(it3 != mapSeenMasternodeBroadcast.end()){ if((*it3).second.lastPing.sigTime < GetTime() - MASTERNODE_REMOVAL_SECONDS*2){ LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode broadcast: hash=%s\n", (*it3).second.GetHash().ToString()); mapSeenMasternodeBroadcast.erase(it3++); } else { ++it3; } } // remove expired mapSeenMasternodePing std::map::iterator it4 = mapSeenMasternodePing.begin(); while(it4 != mapSeenMasternodePing.end()){ if((*it4).second.sigTime < GetTime() - MASTERNODE_REMOVAL_SECONDS*2){ LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode ping: hash=%s\n", (*it4).second.GetHash().ToString()); mapSeenMasternodePing.erase(it4++); } else { ++it4; } } // remove expired mapSeenMasternodeVerification std::map::iterator itv2 = mapSeenMasternodeVerification.begin(); while(itv2 != mapSeenMasternodeVerification.end()){ if((*itv2).second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS){ LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode verification: hash=%s\n", (*itv2).first.ToString()); mapSeenMasternodeVerification.erase(itv2++); } else { ++itv2; } } LogPrintf("CMasternodeMan::CheckAndRemove -- %s\n", ToString()); } void CMasternodeMan::Clear() { LOCK(cs); vMasternodes.clear(); mAskedUsForMasternodeList.clear(); mWeAskedForMasternodeList.clear(); mWeAskedForMasternodeListEntry.clear(); mapSeenMasternodeBroadcast.clear(); mapSeenMasternodePing.clear(); nDsqCount = 0; nLastWatchdogVoteTime = 0; } int CMasternodeMan::CountMasternodes(int nProtocolVersion) { LOCK(cs); int nCount = 0; nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : nProtocolVersion; BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.nProtocolVersion < nProtocolVersion) continue; nCount++; } return nCount; } int CMasternodeMan::CountEnabled(int nProtocolVersion) { LOCK(cs); int nCount = 0; nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : nProtocolVersion; BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.nProtocolVersion < nProtocolVersion || !mn.IsEnabled()) continue; nCount++; } return nCount; } /* Only IPv4 masternodes are allowed in 12.1, saving this for later int CMasternodeMan::CountByIP(int nNetworkType) { LOCK(cs); int nNodeCount = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) if ((nNetworkType == NET_IPV4 && mn.addr.IsIPv4()) || (nNetworkType == NET_TOR && mn.addr.IsTor()) || (nNetworkType == NET_IPV6 && mn.addr.IsIPv6())) { nNodeCount++; } return nNodeCount; } */ void CMasternodeMan::DsegUpdate(CNode* pnode) { LOCK(cs); if(Params().NetworkIDString() == CBaseChainParams::MAIN) { if(!(pnode->addr.IsRFC1918() || pnode->addr.IsLocal())) { std::map::iterator it = mWeAskedForMasternodeList.find(pnode->addr); if(it != mWeAskedForMasternodeList.end() && GetTime() < (*it).second) { LogPrintf("CMasternodeMan::DsegUpdate -- we already asked %s for the list; skipping...\n", pnode->addr.ToString()); return; } } } pnode->PushMessage(NetMsgType::DSEG, CTxIn()); int64_t askAgain = GetTime() + DSEG_UPDATE_SECONDS; mWeAskedForMasternodeList[pnode->addr] = askAgain; LogPrint("masternode", "CMasternodeMan::DsegUpdate -- asked %s for the list\n", pnode->addr.ToString()); } CMasternode* CMasternodeMan::Find(const CScript &payee) { LOCK(cs); BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(GetScriptForDestination(mn.pubKeyCollateralAddress.GetID()) == payee) return &mn; } return NULL; } CMasternode* CMasternodeMan::Find(const CTxIn &vin) { LOCK(cs); BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.vin.prevout == vin.prevout) return &mn; } return NULL; } CMasternode* CMasternodeMan::Find(const CPubKey &pubKeyMasternode) { LOCK(cs); BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.pubKeyMasternode == pubKeyMasternode) return &mn; } return NULL; } bool CMasternodeMan::Get(const CPubKey& pubKeyMasternode, CMasternode& masternode) { // Theses mutexes are recursive so double locking by the same thread is safe. LOCK(cs); CMasternode* pMN = Find(pubKeyMasternode); if(!pMN) { return false; } masternode = *pMN; return true; } bool CMasternodeMan::Get(const CTxIn& vin, CMasternode& masternode) { // Theses mutexes are recursive so double locking by the same thread is safe. LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return false; } masternode = *pMN; return true; } masternode_info_t CMasternodeMan::GetMasternodeInfo(const CTxIn& vin) { masternode_info_t info; LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return info; } info = pMN->GetInfo(); return info; } masternode_info_t CMasternodeMan::GetMasternodeInfo(const CPubKey& pubKeyMasternode) { masternode_info_t info; LOCK(cs); CMasternode* pMN = Find(pubKeyMasternode); if(!pMN) { return info; } info = pMN->GetInfo(); return info; } bool CMasternodeMan::Has(const CTxIn& vin) { LOCK(cs); CMasternode* pMN = Find(vin); return (pMN != NULL); } // // Deterministically select the oldest/best masternode to pay on the network // CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight, bool fFilterSigTime, int& nCount) { // Need LOCK2 here to ensure consistent locking order because the GetBlockHash call below locks cs_main LOCK2(cs_main,cs); CMasternode *pBestMasternode = NULL; std::vector > vecMasternodeLastPaid; /* Make a vector with all of the last paid times */ int nMnCount = CountEnabled(); BOOST_FOREACH(CMasternode &mn, vMasternodes) { mn.Check(); if(!mn.IsEnabled()) continue; // //check protocol version if(mn.nProtocolVersion < mnpayments.GetMinMasternodePaymentsProto()) continue; //it's in the list (up to 8 entries ahead of current block to allow propagation) -- so let's skip it if(mnpayments.IsScheduled(mn, nBlockHeight)) continue; //it's too new, wait for a cycle if(fFilterSigTime && mn.sigTime + (nMnCount*2.6*60) > GetAdjustedTime()) continue; //make sure it has at least as many confirmations as there are masternodes if(mn.GetCollateralAge() < nMnCount) continue; vecMasternodeLastPaid.push_back(std::make_pair(mn.GetLastPaidBlock(), &mn)); } nCount = (int)vecMasternodeLastPaid.size(); //when the network is in the process of upgrading, don't penalize nodes that recently restarted if(fFilterSigTime && nCount < nMnCount/3) return GetNextMasternodeInQueueForPayment(nBlockHeight, false, nCount); // Sort them low to high sort(vecMasternodeLastPaid.begin(), vecMasternodeLastPaid.end(), CompareLastPaidBlock()); uint256 blockHash; if(!GetBlockHash(blockHash, nBlockHeight - 101)) { LogPrintf("CMasternode::GetNextMasternodeInQueueForPayment -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", nBlockHeight - 101); return NULL; } // Look at 1/10 of the oldest nodes (by last payment), calculate their scores and pay the best one // -- This doesn't look at who is being paid in the +8-10 blocks, allowing for double payments very rarely // -- 1/100 payments should be a double payment on mainnet - (1/(3000/10))*2 // -- (chance per block * chances before IsScheduled will fire) int nTenthNetwork = CountEnabled()/10; int nCountTenth = 0; arith_uint256 nHighest = 0; BOOST_FOREACH (PAIRTYPE(int, CMasternode*)& s, vecMasternodeLastPaid){ arith_uint256 nScore = s.second->CalculateScore(blockHash); if(nScore > nHighest){ nHighest = nScore; pBestMasternode = s.second; } nCountTenth++; if(nCountTenth >= nTenthNetwork) break; } return pBestMasternode; } CMasternode* CMasternodeMan::FindRandomNotInVec(const std::vector &vecToExclude, int nProtocolVersion) { LOCK(cs); nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : nProtocolVersion; int nCountEnabled = CountEnabled(nProtocolVersion); int nCountNotExcluded = nCountEnabled - vecToExclude.size(); LogPrintf("CMasternodeMan::FindRandomNotInVec -- %d enabled masternodes, %d masternodes to choose from\n", nCountEnabled, nCountNotExcluded); if(nCountNotExcluded < 1) return NULL; // fill a vector of pointers std::vector vpMasternodesShuffled; BOOST_FOREACH(CMasternode &mn, vMasternodes) { vpMasternodesShuffled.push_back(&mn); } // shuffle pointers std::random_shuffle(vpMasternodesShuffled.begin(), vpMasternodesShuffled.end(), GetInsecureRand); bool fExclude; // loop through BOOST_FOREACH(CMasternode* pmn, vpMasternodesShuffled) { if(pmn->nProtocolVersion < nProtocolVersion || !pmn->IsEnabled()) continue; fExclude = false; BOOST_FOREACH(const CTxIn &txinToExclude, vecToExclude) { if(pmn->vin.prevout == txinToExclude.prevout) { fExclude = true; break; } } if(fExclude) continue; // found the one not in vecToExclude LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec -- found, masternode=%s\n", pmn->vin.prevout.ToStringShort()); return pmn; } LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec -- failed\n"); return NULL; } int CMasternodeMan::GetMasternodeRank(const CTxIn& vin, int nBlockHeight, int nMinProtocol, bool fOnlyActive) { std::vector > vecMasternodeScores; //make sure we know about this block uint256 blockHash = uint256(); if(!GetBlockHash(blockHash, nBlockHeight)) return -1; LOCK(cs); // scan for winner BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.nProtocolVersion < nMinProtocol) continue; if(fOnlyActive) { mn.Check(); if(!mn.IsEnabled()) continue; } int64_t nScore = mn.CalculateScore(blockHash).GetCompact(false); vecMasternodeScores.push_back(std::make_pair(nScore, &mn)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreMN()); int nRank = 0; BOOST_FOREACH (PAIRTYPE(int64_t, CMasternode*)& scorePair, vecMasternodeScores) { nRank++; if(scorePair.second->vin.prevout == vin.prevout) return nRank; } return -1; } std::vector > CMasternodeMan::GetMasternodeRanks(int nBlockHeight, int nMinProtocol) { std::vector > vecMasternodeScores; std::vector > vecMasternodeRanks; //make sure we know about this block uint256 blockHash = uint256(); if(!GetBlockHash(blockHash, nBlockHeight)) return vecMasternodeRanks; LOCK(cs); // scan for winner BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.nProtocolVersion < nMinProtocol || !mn.IsEnabled()) continue; int64_t nScore = mn.CalculateScore(blockHash).GetCompact(false); vecMasternodeScores.push_back(std::make_pair(nScore, &mn)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreMN()); int nRank = 0; BOOST_FOREACH (PAIRTYPE(int64_t, CMasternode*)& s, vecMasternodeScores) { nRank++; vecMasternodeRanks.push_back(std::make_pair(nRank, *s.second)); } return vecMasternodeRanks; } CMasternode* CMasternodeMan::GetMasternodeByRank(int nRank, int nBlockHeight, int nMinProtocol, bool fOnlyActive) { std::vector > vecMasternodeScores; LOCK(cs); uint256 blockHash; if(!GetBlockHash(blockHash, nBlockHeight)) { LogPrintf("CMasternode::GetMasternodeByRank -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", nBlockHeight); return NULL; } // Fill scores BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.nProtocolVersion < nMinProtocol) continue; if(fOnlyActive) { mn.Check(); if(!mn.IsEnabled()) continue; } int64_t nScore = mn.CalculateScore(blockHash).GetCompact(false); vecMasternodeScores.push_back(std::make_pair(nScore, &mn)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreMN()); int rank = 0; BOOST_FOREACH (PAIRTYPE(int64_t, CMasternode*)& s, vecMasternodeScores){ rank++; if(rank == nRank) { return s.second; } } return NULL; } void CMasternodeMan::ProcessMasternodeConnections() { //we don't care about this for regtest if(Params().NetworkIDString() == CBaseChainParams::REGTEST) return; LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { if(pnode->fMasternode) { if(darkSendPool.pSubmittedToMasternode != NULL && pnode->addr == darkSendPool.pSubmittedToMasternode->addr) continue; LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->id, pnode->addr.ToString()); pnode->fDisconnect = true; } } } void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if(fLiteMode) return; // disable all Dash specific functionality if(!masternodeSync.IsBlockchainSynced()) return; if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast CMasternodeBroadcast mnb; vRecv >> mnb; int nDos = 0; if (CheckMnbAndUpdateMasternodeList(mnb, nDos)) { // use announced Masternode as a peer addrman.Add(CAddress(mnb.addr), pfrom->addr, 2*60*60); } else if(nDos > 0) { Misbehaving(pfrom->GetId(), nDos); } } else if (strCommand == NetMsgType::MNPING) { //Masternode Ping // ignore masternode pings until masternode list is synced if (!masternodeSync.IsMasternodeListSynced()) return; CMasternodePing mnp; vRecv >> mnp; LogPrint("masternode", "MNPING -- Masternode ping, masternode=%s\n", mnp.vin.prevout.ToStringShort()); LOCK(cs); if(mapSeenMasternodePing.count(mnp.GetHash())) return; //seen mapSeenMasternodePing.insert(std::make_pair(mnp.GetHash(), mnp)); LogPrint("masternode", "MNPING -- Masternode ping, masternode=%s new\n", mnp.vin.prevout.ToStringShort()); int nDos = 0; if(mnp.CheckAndUpdate(nDos, false)) return; if(nDos > 0) { // if anything significant failed, mark that node Misbehaving(pfrom->GetId(), nDos); } else { // if nothing significant failed, search existing Masternode list CMasternode* pmn = Find(mnp.vin); // if it's known, don't ask for the mnb, just return if(pmn != NULL) return; } // something significant is broken or mn is unknown, // we might have to ask for a masternode entry once AskForMN(pfrom, mnp.vin); } else if (strCommand == NetMsgType::DSEG) { //Get Masternode list or specific entry // 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. if (!masternodeSync.IsSynced()) return; CTxIn vin; vRecv >> vin; LogPrint("masternode", "DSEG -- Masternode list, masternode=%s\n", vin.prevout.ToStringShort()); LOCK(cs); if(vin == CTxIn()) { //only should ask for this once //local network bool isLocal = (pfrom->addr.IsRFC1918() || pfrom->addr.IsLocal()); if(!isLocal && Params().NetworkIDString() == CBaseChainParams::MAIN) { std::map::iterator i = mAskedUsForMasternodeList.find(pfrom->addr); if (i != mAskedUsForMasternodeList.end()){ int64_t t = (*i).second; if (GetTime() < t) { Misbehaving(pfrom->GetId(), 34); LogPrintf("DSEG -- peer already asked me for the list, peer=%d\n", pfrom->id); return; } } int64_t askAgain = GetTime() + DSEG_UPDATE_SECONDS; mAskedUsForMasternodeList[pfrom->addr] = askAgain; } } //else, asking for a specific node which is ok int nInvCount = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) { if (vin != CTxIn() && vin != mn.vin) continue; // asked for specific vin but we are not there yet if (mn.addr.IsRFC1918() || mn.addr.IsLocal()) continue; // do not send local network masternode if (!mn.IsEnabled()) continue; LogPrint("masternode", "DSEG -- Sending Masternode entry: masternode=%s addr=%s\n", mn.vin.prevout.ToStringShort(), mn.addr.ToString()); CMasternodeBroadcast mnb = CMasternodeBroadcast(mn); uint256 hash = mnb.GetHash(); pfrom->PushInventory(CInv(MSG_MASTERNODE_ANNOUNCE, hash)); nInvCount++; if (!mapSeenMasternodeBroadcast.count(hash)) { mapSeenMasternodeBroadcast.insert(std::make_pair(hash, mnb)); } if (vin == mn.vin) { LogPrintf("DSEG -- Sent 1 Masternode inv to peer %d\n", pfrom->id); return; } } if(vin == CTxIn()) { pfrom->PushMessage(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_LIST, nInvCount); LogPrintf("DSEG -- Sent %d Masternode invs to peer %d\n", nInvCount, pfrom->id); return; } // smth weird happen - someone asked us for vin we have no idea about? LogPrint("masternode", "DSEG -- No invs sent to peer %d\n", pfrom->id); } else if (strCommand == NetMsgType::MNVERIFY) { // Masternode Verify CMasternodeVerification mnv; vRecv >> mnv; if(mnv.vchSig1.empty()) { // CASE 1: someone asked me to verify myself /IP we are using/ SendVerifyReply(pfrom, mnv); } else if (mnv.vchSig2.empty()) { // CASE 2: we _probably_ got verification we requested from some masternode ProcessVerifyReply(pfrom, mnv); } else { // CASE 3: we _probably_ got verification broadcast signed by some masternode which verified another one ProcessVerifyBroadcast(pfrom, mnv); } } } // Verification of masternodes via unique direct requests. void CMasternodeMan::DoFullVerificationStep() { if(activeMasternode.vin == CTxIn()) return; std::vector > vecMasternodeRanks = GetMasternodeRanks(pCurrentBlockIndex->nHeight - 1, MIN_POSE_PROTO_VERSION); LOCK(cs); int nCount = 0; int nCountMax = std::max(10, (int)vMasternodes.size() / 100); // verify at least 10 masternode at once but at most 1% of all known masternodes int nMyRank = -1; int nRanksTotal = (int)vecMasternodeRanks.size(); // send verify requests only if we are in top MAX_POSE_RANK std::vector >::iterator it = vecMasternodeRanks.begin(); while(it != vecMasternodeRanks.end()) { if(it->first > MAX_POSE_RANK) { LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Must be in top %d to send verify request\n", (int)MAX_POSE_RANK); return; } if(it->second.vin == activeMasternode.vin) { nMyRank = it->first; LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Found self at rank %d/%d, verifying up to %d masternodes\n", nMyRank, nRanksTotal, nCountMax); break; } ++it; } // edge case: list is too short and this masternode is not enabled if(nMyRank == -1) return; // send verify requests to up to nCountMax masternodes starting from // (MAX_POSE_RANK + nCountMax * (nMyRank - 1) + 1) int nOffset = MAX_POSE_RANK + nCountMax * (nMyRank - 1); if(nOffset >= (int)vecMasternodeRanks.size()) return; std::vector vSortedByAddr; BOOST_FOREACH(CMasternode& mn, vMasternodes) { vSortedByAddr.push_back(&mn); } sort(vSortedByAddr.begin(), vSortedByAddr.end(), CompareByAddr()); it = vecMasternodeRanks.begin() + nOffset; while(it != vecMasternodeRanks.end()) { if(it->second.IsPoSeVerified() || it->second.IsPoSeBanned()) { LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Already %s%s%s masternode %s address %s, skipping...\n", it->second.IsPoSeVerified() ? "verified" : "", it->second.IsPoSeVerified() && it->second.IsPoSeBanned() ? " and " : "", it->second.IsPoSeBanned() ? "banned" : "", it->second.vin.prevout.ToStringShort(), it->second.addr.ToString()); ++it; continue; } LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Verifying masternode %s rank %d/%d address %s\n", it->second.vin.prevout.ToStringShort(), it->first, nRanksTotal, it->second.addr.ToString()); if(SendVerifyRequest((CAddress)it->second.addr, vSortedByAddr)) { nCount++; if(nCount >= nCountMax) break; } ++it; } LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Sent verification requests to %d masternodes\n", nCount); } // This function tries to find masternodes with the same addr, // find a verified one and ban all the other. If there are many nodes // with the same addr but none of them is verified yet, then none of them are banned. // It could take many times to run this before most of the duplicate nodes are banned. void CMasternodeMan::CheckSameAddr() { if(!masternodeSync.IsSynced() || vMasternodes.empty()) return; std::vector vBan; std::vector vSortedByAddr; { LOCK(cs); CMasternode* pprevMasternode = NULL; CMasternode* pverifiedMasternode = NULL; BOOST_FOREACH(CMasternode& mn, vMasternodes) { vSortedByAddr.push_back(&mn); } sort(vSortedByAddr.begin(), vSortedByAddr.end(), CompareByAddr()); BOOST_FOREACH(CMasternode* pmn, vSortedByAddr) { // check only (pre)enabled masternodes if(!pmn->IsEnabled() && !pmn->IsPreEnabled()) continue; // initial step if(!pprevMasternode) { pprevMasternode = pmn; pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; continue; } // second+ step if(pmn->addr == pprevMasternode->addr) { if(pverifiedMasternode) { // another masternode with the same ip is verified, ban this one vBan.push_back(pmn); } else if(pmn->IsPoSeVerified()) { // this masternode with the same ip is verified, ban previous one vBan.push_back(pprevMasternode); // and keep a reference to be able to ban following masternodes with the same ip pverifiedMasternode = pmn; } } else { pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; } pprevMasternode = pmn; } } // ban duplicates BOOST_FOREACH(CMasternode* pmn, vBan) { pmn->nPoSeBanScore++; } } bool CMasternodeMan::SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr) { if(netfulfilledman.HasFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { // we already asked for verification, not a good idea to do this too often, skip it LogPrint("masternode", "CMasternodeMan::SendVerifyRequest -- too many requests, skipping... addr=%s\n", addr.ToString()); return false; } CNode* pnode = ConnectNode(addr, NULL, true); if(pnode != NULL) { netfulfilledman.AddFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request"); // use random nonce, store it and require node to reply with correct one later CMasternodeVerification mnv(addr, GetInsecureRand(999999), pCurrentBlockIndex->nHeight - 1); mWeAskedForVerification[addr] = mnv; LogPrintf("CMasternodeMan::SendVerifyRequest -- verifying using nonce %d addr=%s\n", mnv.nonce, addr.ToString()); pnode->PushMessage(NetMsgType::MNVERIFY, mnv); return true; } else { // can't connect, add some PoSe "ban score" to all masternodes with given addr bool fFound = false; BOOST_FOREACH(CMasternode* pmn, vSortedByAddr) { if(pmn->addr != addr) { if(fFound) break; continue; } fFound = true; pmn->nPoSeBanScore++; } return false; } } void CMasternodeMan::SendVerifyReply(CNode* pnode, CMasternodeVerification& mnv) { // only masternodes can sign this, why would someone ask regular node? if(!fMasterNode) { // do not ban, malicious node might be using my IP // and trying to confuse the node which tries to verify it return; } if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply")) { // peer should not ask us that often LogPrintf("MasternodeMan::SendVerifyReply -- ERROR: peer already asked me recently, peer=%d\n", pnode->id); Misbehaving(pnode->id, 20); return; } uint256 blockHash; if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { LogPrintf("MasternodeMan::SendVerifyReply -- can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); return; } std::string strMessage = strprintf("%s%d%s", activeMasternode.service.ToString(false), mnv.nonce, blockHash.ToString()); if(!darkSendSigner.SignMessage(strMessage, mnv.vchSig1, activeMasternode.keyMasternode)) { LogPrintf("MasternodeMan::SendVerifyReply -- SignMessage() failed\n"); return; } std::string strError; if(!darkSendSigner.VerifyMessage(activeMasternode.pubKeyMasternode, mnv.vchSig1, strMessage, strError)) { LogPrintf("MasternodeMan::SendVerifyReply -- VerifyMessage() failed, error: %s\n", strError); return; } pnode->PushMessage(NetMsgType::MNVERIFY, mnv); netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply"); } void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& mnv) { std::string strError; // did we even ask for it? if that's the case we should have matching fulfilled request if(!netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: we didn't ask for verification of %s, peer=%d\n", pnode->addr.ToString(), pnode->id); Misbehaving(pnode->id, 20); return; } // Received nonce for a known address must match the one we sent if(mWeAskedForVerification[pnode->addr].nonce != mnv.nonce) { LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: wrong nounce: requested=%d, received=%d, peer=%d\n", mWeAskedForVerification[pnode->addr].nonce, mnv.nonce, pnode->id); Misbehaving(pnode->id, 20); return; } // Received nBlockHeight for a known address must match the one we sent if(mWeAskedForVerification[pnode->addr].nBlockHeight != mnv.nBlockHeight) { LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: wrong nBlockHeight: requested=%d, received=%d, peer=%d\n", mWeAskedForVerification[pnode->addr].nBlockHeight, mnv.nBlockHeight, pnode->id); Misbehaving(pnode->id, 20); return; } uint256 blockHash; if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { // this shouldn't happen... LogPrintf("MasternodeMan::ProcessVerifyReply -- can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); return; } // we already verified this address, why node is spamming? if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done")) { LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: already verified %s recently\n", pnode->addr.ToString()); Misbehaving(pnode->id, 20); return; } { LOCK(cs); CMasternode* prealMasternode = NULL; std::vector vpMasternodesToBan; std::vector::iterator it = vMasternodes.begin(); std::string strMessage1 = strprintf("%s%d%s", pnode->addr.ToString(false), mnv.nonce, blockHash.ToString()); while(it != vMasternodes.end()) { if((CAddress)it->addr == pnode->addr) { if(darkSendSigner.VerifyMessage(it->pubKeyMasternode, mnv.vchSig1, strMessage1, strError)) { // found it! prealMasternode = &(*it); if(!it->IsPoSeVerified()) { it->nPoSeBanScore--; } netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done"); // we can only broadcast it if we are an activated masternode if(activeMasternode.vin == CTxIn()) continue; // update ... mnv.addr = it->addr; mnv.vin1 = it->vin; mnv.vin2 = activeMasternode.vin; std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString(), mnv.vin1.prevout.ToStringShort(), mnv.vin2.prevout.ToStringShort()); // ... and sign it if(!darkSendSigner.SignMessage(strMessage2, mnv.vchSig2, activeMasternode.keyMasternode)) { LogPrintf("MasternodeMan::ProcessVerifyReply -- SignMessage() failed\n"); return; } std::string strError; if(!darkSendSigner.VerifyMessage(activeMasternode.pubKeyMasternode, mnv.vchSig2, strMessage2, strError)) { LogPrintf("MasternodeMan::ProcessVerifyReply -- VerifyMessage() failed, error: %s\n", strError); return; } mWeAskedForVerification[pnode->addr] = mnv; mnv.Relay(); } else { vpMasternodesToBan.push_back(&(*it)); } } ++it; } // no real masternode found?... if(!prealMasternode) { // this should never be the case normally, // only if someone is trying to game the system in some way or smth like that LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: no real masternode found for addr %s\n", pnode->addr.ToString()); Misbehaving(pnode->id, 20); return; } LogPrintf("CMasternodeMan::ProcessVerifyReply -- verified real masternode %s for addr %s\n", prealMasternode->vin.prevout.ToStringShort(), pnode->addr.ToString()); // increase ban score for everyone else BOOST_FOREACH(CMasternode* pmn, vpMasternodesToBan) { pmn->nPoSeBanScore++; LogPrint("masternode", "CMasternodeMan::ProcessVerifyBroadcast -- increased PoSe ban score for %s addr %s, new score %d\n", prealMasternode->vin.prevout.ToStringShort(), pnode->addr.ToString(), pmn->nPoSeBanScore); } LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- PoSe score incresed for %d fake masternodes, addr %s\n", (int)vpMasternodesToBan.size(), pnode->addr.ToString()); } } void CMasternodeMan::ProcessVerifyBroadcast(CNode* pnode, const CMasternodeVerification& mnv) { std::string strError; if(mapSeenMasternodeVerification.find(mnv.GetHash()) != mapSeenMasternodeVerification.end()) { // we already have one return; } mapSeenMasternodeVerification[mnv.GetHash()] = mnv; // we don't care about history if(mnv.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) { LogPrint("masternode", "MasternodeMan::ProcessVerifyBroadcast -- Outdated: current block %d, verification block %d, peer=%d\n", pCurrentBlockIndex->nHeight, mnv.nBlockHeight, pnode->id); return; } if(mnv.vin1.prevout == mnv.vin2.prevout) { LogPrint("masternode", "MasternodeMan::ProcessVerifyBroadcast -- ERROR: same vins %s, peer=%d\n", mnv.vin1.prevout.ToStringShort(), pnode->id); // that was NOT a good idea to cheat and verify itself, // ban the node we received such message from Misbehaving(pnode->id, 100); return; } uint256 blockHash; if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { // this shouldn't happen... LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- Can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); return; } int nRank = GetMasternodeRank(mnv.vin2, mnv.nBlockHeight, MIN_POSE_PROTO_VERSION); if(nRank < MAX_POSE_RANK) { LogPrint("masternode", "MasternodeMan::ProcessVerifyBroadcast -- Mastrernode is not in top %d, current rank %d, peer=%d\n", (int)MAX_POSE_RANK, nRank, pnode->id); return; } { LOCK(cs); std::string strMessage1 = strprintf("%s%d%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString()); std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString(), mnv.vin1.prevout.ToStringShort(), mnv.vin2.prevout.ToStringShort()); CMasternode* pmn1 = Find(mnv.vin1); if(!pmn1) { LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- can't find masternode1 %s\n", mnv.vin1.prevout.ToStringShort()); return; } CMasternode* pmn2 = Find(mnv.vin2); if(!pmn2) { LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- can't find masternode2 %s\n", mnv.vin2.prevout.ToStringShort()); return; } if(pmn1->addr != mnv.addr) { LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- addr %s do not match %s\n", mnv.addr.ToString(), pnode->addr.ToString()); return; } if(darkSendSigner.VerifyMessage(pmn1->pubKeyMasternode, mnv.vchSig1, strMessage1, strError)) { LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- VerifyMessage() for masternode1 failed, error: %s\n", strError); return; } if(darkSendSigner.VerifyMessage(pmn2->pubKeyMasternode, mnv.vchSig2, strMessage2, strError)) { LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- VerifyMessage() for masternode2 failed, error: %s\n", strError); return; } if(!pmn1->IsPoSeVerified()) { pmn1->nPoSeBanScore--; } mnv.Relay(); LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- verified masternode %s for addr %s\n", pmn1->vin.prevout.ToStringShort(), pnode->addr.ToString()); // increase ban score for everyone else with the same addr int nCount = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.addr != mnv.addr || mn.vin.prevout == mnv.vin1.prevout) continue; mn.nPoSeBanScore++; nCount++; LogPrint("masternode", "CMasternodeMan::ProcessVerifyBroadcast -- increased PoSe ban score for %s addr %s, new score %d\n", mn.vin.prevout.ToStringShort(), mn.addr.ToString(), mn.nPoSeBanScore); } LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- PoSe score incresed for %d fake masternodes, addr %s\n", nCount, pnode->addr.ToString()); } } void CMasternodeMan::Remove(CTxIn vin) { LOCK(cs); std::vector::iterator it = vMasternodes.begin(); while(it != vMasternodes.end()) { if((*it).vin == vin) { LogPrint("masternode", "CMasternodeMan::Remove -- Removing Masternode: %s addr=%s, %i now\n", vin.prevout.ToStringShort(), (*it).addr.ToString(), size() - 1); vMasternodes.erase(it); break; } ++it; } } std::string CMasternodeMan::ToString() const { std::ostringstream info; info << "Masternodes: " << (int)vMasternodes.size() << ", peers who asked us for Masternode list: " << (int)mAskedUsForMasternodeList.size() << ", peers we asked for Masternode list: " << (int)mWeAskedForMasternodeList.size() << ", entries in Masternode list we asked for: " << (int)mWeAskedForMasternodeListEntry.size() << ", nDsqCount: " << (int)nDsqCount; return info.str(); } int CMasternodeMan::GetEstimatedMasternodes(int nBlock) { /* Masternodes = (Coins/1000)*X on average *X = nPercentage, starting at 0.52 nPercentage goes up 0.01 each period Period starts at 35040, which has exponential slowing growth */ int nPercentage = 52; //0.52 int nPeriod = 35040; int nCollateral = 1000; for (int i = nPeriod; i <= nBlock; i += nPeriod) { nPercentage++; nPeriod*=2; } return (GetTotalCoinEstimate(nBlock)/100*nPercentage/nCollateral); } void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast mnb) { LOCK(cs); mapSeenMasternodePing.insert(std::make_pair(mnb.lastPing.GetHash(), mnb.lastPing)); mapSeenMasternodeBroadcast.insert(std::make_pair(mnb.GetHash(), mnb)); LogPrintf("CMasternodeMan::UpdateMasternodeList -- masternode=%s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); CMasternode* pmn = Find(mnb.vin); if(pmn == NULL) { CMasternode mn(mnb); if(Add(mn)) { masternodeSync.AddedMasternodeList(); } } else if(pmn->UpdateFromNewBroadcast(mnb)) { masternodeSync.AddedMasternodeList(); } } bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, int& nDos) { LOCK(cs); nDos = 0; LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- masternode=%s\n", mnb.vin.prevout.ToStringShort()); if(mapSeenMasternodeBroadcast.count(mnb.GetHash())) { //seen return true; } mapSeenMasternodeBroadcast.insert(std::make_pair(mnb.GetHash(), mnb)); LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- masternode=%s new\n", mnb.vin.prevout.ToStringShort()); if(!mnb.SimpleCheck(nDos)) { LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- SimpleCheck() failed, masternode=%s\n", mnb.vin.prevout.ToStringShort()); return false; } // search Masternode list CMasternode* pmn = Find(mnb.vin); if(pmn) { if(!mnb.Update(pmn, nDos)) { LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- Update() failed, masternode=%s\n", mnb.vin.prevout.ToStringShort()); return false; } } else { if(mnb.CheckOutpoint(nDos)) { Add(mnb); masternodeSync.AddedMasternodeList(); // if it matches our Masternode privkey... if(fMasterNode && mnb.pubKeyMasternode == activeMasternode.pubKeyMasternode) { mnb.nPoSeBanScore = -MASTERNODE_POSE_BAN_MAX_SCORE; if(mnb.nProtocolVersion == PROTOCOL_VERSION) { // ... and PROTOCOL_VERSION, then we've been remotely activated ... LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList -- Got NEW Masternode entry: masternode=%s sigTime=%lld addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.sigTime, mnb.addr.ToString()); activeMasternode.ManageState(); } else { // ... otherwise we need to reactivate our node, do not add it to the list and do not relay // but also do not ban the node we get this message from LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList -- wrong PROTOCOL_VERSION, re-activate your MN: message nProtocolVersion=%d PROTOCOL_VERSION=%d\n", mnb.nProtocolVersion, PROTOCOL_VERSION); return false; } } mnb.Relay(); } else { LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList -- Rejected Masternode entry: %s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); return false; } } return true; } void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex) { LOCK(cs); if(fLiteMode) return; static bool IsFirstRun = true; // Do full scan on first run or if we are not a masternode // (MNs should update this info on every block, so limited scan should be enough for them) int nMaxBlocksToScanBack = (IsFirstRun || !fMasterNode) ? mnpayments.GetStorageLimit() : LAST_PAID_SCAN_BLOCKS; // LogPrint("mnpayments", "CMasternodeMan::UpdateLastPaid -- nHeight=%d, nMaxBlocksToScanBack=%d, IsFirstRun=%s\n", // pindex->nHeight, nMaxBlocksToScanBack, IsFirstRun ? "true" : "false"); BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.UpdateLastPaid(pindex, nMaxBlocksToScanBack); } // every time is like the first time if winners list is not synced IsFirstRun = !masternodeSync.IsWinnersListSynced(); } void CMasternodeMan::UpdateWatchdogVoteTime(const CTxIn& vin) { LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return; } pMN->UpdateWatchdogVoteTime(); nLastWatchdogVoteTime = GetTime(); } bool CMasternodeMan::IsWatchdogActive() { LOCK(cs); // Check if any masternodes have voted recently, otherwise return false return (GetTime() - nLastWatchdogVoteTime) <= MASTERNODE_WATCHDOG_MAX_SECONDS; } void CMasternodeMan::AddGovernanceVote(const CTxIn& vin, uint256 nGovernanceObjectHash) { LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return; } pMN->AddGovernanceVote(nGovernanceObjectHash); } void CMasternodeMan::RemoveGovernanceObject(uint256 nGovernanceObjectHash) { LOCK(cs); BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.RemoveGovernanceObject(nGovernanceObjectHash); } } void CMasternodeMan::CheckMasternode(const CTxIn& vin, bool fForce) { LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return; } pMN->Check(fForce); } void CMasternodeMan::CheckMasternode(const CPubKey& pubKeyMasternode, bool fForce) { LOCK(cs); CMasternode* pMN = Find(pubKeyMasternode); if(!pMN) { return; } pMN->Check(fForce); } int CMasternodeMan::GetMasternodeState(const CTxIn& vin) { LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return CMasternode::MASTERNODE_REMOVE; } return pMN->nActiveState; } int CMasternodeMan::GetMasternodeState(const CPubKey& pubKeyMasternode) { LOCK(cs); CMasternode* pMN = Find(pubKeyMasternode); if(!pMN) { return CMasternode::MASTERNODE_REMOVE; } return pMN->nActiveState; } bool CMasternodeMan::IsMasternodePingedWithin(const CTxIn& vin, int nSeconds, int64_t nTimeToCheckAt) { LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return false; } return pMN->IsPingedWithin(nSeconds, nTimeToCheckAt); } void CMasternodeMan::SetMasternodeLastPing(const CTxIn& vin, const CMasternodePing& mnp) { LOCK(cs); CMasternode* pMN = Find(vin); if(!pMN) { return; } pMN->lastPing = mnp; mapSeenMasternodePing.insert(std::make_pair(mnp.GetHash(), mnp)); CMasternodeBroadcast mnb(*pMN); uint256 hash = mnb.GetHash(); if(mapSeenMasternodeBroadcast.count(hash)) { mapSeenMasternodeBroadcast[hash].lastPing = mnp; } } void CMasternodeMan::UpdatedBlockTip(const CBlockIndex *pindex) { pCurrentBlockIndex = pindex; LogPrint("masternode", "CMasternodeMan::UpdatedBlockTip -- pCurrentBlockIndex->nHeight=%d\n", pCurrentBlockIndex->nHeight); CheckSameAddr(); if(fMasterNode) { DoFullVerificationStep(); // normal wallet does not need to update this every block, doing update on rpc call should be enough UpdateLastPaid(pindex); } }