// 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 "util.h" #include "addrman.h" #include "spork.h" #include #include /** Masternode manager */ CMasternodeMan mnodeman; struct CompareLastPaid { bool operator()(const pair& t1, const pair& t2) const { return t1.first < t2.first; } }; struct CompareScoreTxIn { bool operator()(const pair& t1, const pair& t2) const { return t1.first < t2.first; } }; struct CompareScoreMN { bool operator()(const pair& t1, const pair& t2) const { return t1.first < t2.first; } }; CMasternodeMan::CMasternodeMan() { nDsqCount = 0; } 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: Adding new Masternode %s - %i now\n", mn.addr.ToString(), size() + 1); vMasternodes.push_back(mn); return true; } return false; } void CMasternodeMan::AskForMN(CNode* pnode, CTxIn &vin) { std::map::iterator i = mWeAskedForMasternodeListEntry.find(vin.prevout); if (i != mWeAskedForMasternodeListEntry.end()) { int64_t t = (*i).second; if (GetTime() < t) return; // we've asked recently } // ask for the mnb info once from the node that sent mnp LogPrintf("CMasternodeMan::AskForMN - Asking node for missing entry, vin: %s\n", vin.ToString()); pnode->PushMessage(NetMsgType::DSEG, vin); int64_t askAgain = GetTime() + MASTERNODE_MIN_MNP_SECONDS; mWeAskedForMasternodeListEntry[vin.prevout] = askAgain; } void CMasternodeMan::Check() { LOCK(cs); BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); } } void CMasternodeMan::CheckAndRemove(bool forceExpiredRemoval) { LogPrintf("CMasternodeMan::CheckAndRemove\n"); Check(); LOCK(cs); //remove inactive and outdated vector::iterator it = vMasternodes.begin(); while(it != vMasternodes.end()){ if((*it).activeState == CMasternode::MASTERNODE_REMOVE || (*it).activeState == CMasternode::MASTERNODE_VIN_SPENT || (forceExpiredRemoval && (*it).activeState == CMasternode::MASTERNODE_EXPIRED)) { LogPrint("masternode", "CMasternodeMan::CheckAndRemove - Removing %s Masternode %s - %i now\n", (*it).Status(), (*it).addr.ToString(), size() - 1); //erase all of the broadcasts we've seen from this vin // -- if we missed a few pings and the node was removed, this will allow is to get it back without them // sending a brand new mnb map::iterator it3 = mapSeenMasternodeBroadcast.begin(); while(it3 != mapSeenMasternodeBroadcast.end()){ if((*it3).second.vin == (*it).vin){ masternodeSync.mapSeenSyncMNB.erase((*it3).first); mapSeenMasternodeBroadcast.erase(it3++); } else { ++it3; } } // allow us to ask for this masternode again if we see another ping map::iterator it2 = mWeAskedForMasternodeListEntry.begin(); while(it2 != mWeAskedForMasternodeListEntry.end()){ if((*it2).first == (*it).vin.prevout){ mWeAskedForMasternodeListEntry.erase(it2++); } else { ++it2; } } it = vMasternodes.erase(it); } else { ++it; } } // check who's asked for the Masternode list 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 map::iterator it2 = mWeAskedForMasternodeListEntry.begin(); while(it2 != mWeAskedForMasternodeListEntry.end()){ if((*it2).second < GetTime()){ mWeAskedForMasternodeListEntry.erase(it2++); } else { ++it2; } } // remove expired mapSeenMasternodeBroadcast 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 %s\n", (*it3).second.GetHash().ToString()); masternodeSync.mapSeenSyncMNB.erase((*it3).second.GetHash()); mapSeenMasternodeBroadcast.erase(it3++); } else { ++it3; } } // remove expired mapSeenMasternodePing 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 %s\n", (*it4).second.GetHash().ToString()); mapSeenMasternodePing.erase(it4++); } else { ++it4; } } } void CMasternodeMan::Clear() { LOCK(cs); vMasternodes.clear(); mAskedUsForMasternodeList.clear(); mWeAskedForMasternodeList.clear(); mWeAskedForMasternodeListEntry.clear(); mapSeenMasternodeBroadcast.clear(); mapSeenMasternodePing.clear(); nDsqCount = 0; } int CMasternodeMan::CountEnabled(int protocolVersion) { int i = 0; protocolVersion = protocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : protocolVersion; BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.protocolVersion < protocolVersion || !mn.IsEnabled()) continue; i++; } return i; } int CMasternodeMan::CountByIP(int nNetworkType) { 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()) { if (GetTime() < (*it).second) { LogPrintf("dseg - we already asked %s for the list; skipping...\n", pnode->addr.ToString()); return; } } } } pnode->PushMessage(NetMsgType::DSEG, CTxIn()); int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; mWeAskedForMasternodeList[pnode->addr] = askAgain; } CMasternode *CMasternodeMan::Find(const CScript &payee) { LOCK(cs); CScript payee2; BOOST_FOREACH(CMasternode& mn, vMasternodes) { payee2 = GetScriptForDestination(mn.pubkey.GetID()); if(payee2 == 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.pubkey2 == pubKeyMasternode) return &mn; } return NULL; } // // Deterministically select the oldest/best masternode to pay on the network // CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight, bool fFilterSigTime, int& nCount) { LOCK(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.protocolVersion < 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 as many confirmations as there are masternodes if(mn.GetMasternodeInputAge() < nMnCount) continue; vecMasternodeLastPaid.push_back(make_pair(mn.SecondsSincePayment(), mn.vin)); } 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 high to low sort(vecMasternodeLastPaid.rbegin(), vecMasternodeLastPaid.rend(), CompareLastPaid()); // 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 nHigh = 0; BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn)& s, vecMasternodeLastPaid){ CMasternode* pmn = Find(s.second); if(!pmn) break; arith_uint256 n = UintToArith256(pmn->CalculateScore(1, nBlockHeight - 101)); if(n > nHigh){ nHigh = n; pBestMasternode = pmn; } nCountTenth++; if(nCountTenth >= nTenthNetwork) break; } return pBestMasternode; } CMasternode *CMasternodeMan::FindRandomNotInVec(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 aren't yet exluded\n", nCountEnabled, nCountNotExcluded); if(nCountNotExcluded < 1) return NULL; std::vector vMasternodesShuffled = vMasternodes; std::random_shuffle(vMasternodesShuffled.begin(), vMasternodesShuffled.end(), GetRandInt); bool fExclude; BOOST_FOREACH(CMasternode &mn, vMasternodesShuffled) { if(mn.protocolVersion < nProtocolVersion || !mn.IsEnabled()) continue; fExclude = false; BOOST_FOREACH(CTxIn &txinToExclude, vecToExclude) { if(mn.vin.prevout == txinToExclude.prevout) { fExclude = true; break; } } if(fExclude) continue; // found the one not in vecToExclude return Find(mn.vin); } return NULL; } CMasternode* CMasternodeMan::GetCurrentMasterNode(int mod, int64_t nBlockHeight, int minProtocol) { int64_t score = 0; CMasternode* winner = NULL; // scan for winner BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.protocolVersion < minProtocol || !mn.IsEnabled()) continue; // calculate the score for each Masternode uint256 n = mn.CalculateScore(mod, nBlockHeight); int64_t n2 = UintToArith256(n).GetCompact(false); // determine the winner if(n2 > score){ score = n2; winner = &mn; } } return winner; } int CMasternodeMan::GetMasternodeRank(const CTxIn& vin, int64_t nBlockHeight, int minProtocol, bool fOnlyActive) { std::vector > vecMasternodeScores; //make sure we know about this block uint256 hash = uint256(); if(!GetBlockHash(hash, nBlockHeight)) return -1; // scan for winner BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.protocolVersion < minProtocol) continue; if(fOnlyActive) { mn.Check(); if(!mn.IsEnabled()) continue; } uint256 n = mn.CalculateScore(1, nBlockHeight); int64_t n2 = UintToArith256(n).GetCompact(false); vecMasternodeScores.push_back(make_pair(n2, mn.vin)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreTxIn()); int rank = 0; BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn)& s, vecMasternodeScores){ rank++; if(s.second.prevout == vin.prevout) { return rank; } } return -1; } std::vector > CMasternodeMan::GetMasternodeRanks(int64_t nBlockHeight, int minProtocol) { std::vector > vecMasternodeScores; std::vector > vecMasternodeRanks; //make sure we know about this block uint256 hash = uint256(); if(!GetBlockHash(hash, nBlockHeight)) return vecMasternodeRanks; // scan for winner BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.protocolVersion < minProtocol) continue; if(!mn.IsEnabled()) { continue; } uint256 n = mn.CalculateScore(1, nBlockHeight); int64_t n2 = UintToArith256(n).GetCompact(false); vecMasternodeScores.push_back(make_pair(n2, mn)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreMN()); int rank = 0; BOOST_FOREACH (PAIRTYPE(int64_t, CMasternode)& s, vecMasternodeScores){ rank++; vecMasternodeRanks.push_back(make_pair(rank, s.second)); } return vecMasternodeRanks; } CMasternode* CMasternodeMan::GetMasternodeByRank(int nRank, int64_t nBlockHeight, int minProtocol, bool fOnlyActive) { std::vector > vecMasternodeScores; // scan for winner BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.protocolVersion < minProtocol) continue; if(fOnlyActive) { mn.Check(); if(!mn.IsEnabled()) continue; } uint256 n = mn.CalculateScore(1, nBlockHeight); int64_t n2 = UintToArith256(n).GetCompact(false); vecMasternodeScores.push_back(make_pair(n2, mn.vin)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreTxIn()); int rank = 0; BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn)& s, vecMasternodeScores){ rank++; if(rank == nRank) { return Find(s.second); } } return NULL; } void CMasternodeMan::InitDummyScriptPubkey() { CKey secret; secret.MakeNewKey(true); CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); if (pubkey.IsValid()) { CKeyID keyID = pubkey.GetID(); LogPrintf("Generated dummyScriptPubkey: address %s privkey %s\n", CBitcoinAddress(keyID).ToString(), CBitcoinSecret(secret).ToString()); dummyScriptPubkey = GetScriptForDestination(keyID); } else { LogPrintf("CMasternodeMan::InitDummyScriptPubkey - ERROR: can't assign dummyScriptPubkey\n"); } } 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 %s \n", pnode->addr.ToString()); pnode->fDisconnect = true; } } } void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if(fLiteMode) return; //disable all Darksend/Masternode related functionality if(!masternodeSync.IsBlockchainSynced()) return; LOCK(cs_process_message); 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", "mnp - Masternode ping, vin: %s\n", mnp.vin.ToString()); if(mapSeenMasternodePing.count(mnp.GetHash())) return; //seen mapSeenMasternodePing.insert(make_pair(mnp.GetHash(), mnp)); LogPrint("masternode", "mnp - Masternode ping, vin: %s new\n", mnp.vin.ToString()); int nDos = 0; if(mnp.CheckAndUpdate(nDos)) 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, vin: %s\n", vin.ToString()); 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\n"); return; } } int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; mAskedUsForMasternodeList[pfrom->addr] = askAgain; } } //else, asking for a specific node which is ok int nInvCount = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.addr.IsRFC1918() || mn.addr.IsLocal()) continue; //local network if(mn.IsEnabled()) { if(vin == CTxIn() || vin == mn.vin){ LogPrint("masternode", "dseg - Sending Masternode entry - %s \n", 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(make_pair(hash, mnb)); if(vin == mn.vin) { LogPrintf("dseg - Sent 1 Masternode entries to %s\n", pfrom->addr.ToString()); return; } } } } if(vin == CTxIn()) { pfrom->PushMessage(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_LIST, nInvCount); LogPrintf("dseg - Sent %d Masternode entries to %s\n", nInvCount, pfrom->addr.ToString()); } } } void CMasternodeMan::Remove(CTxIn vin) { LOCK(cs); vector::iterator it = vMasternodes.begin(); while(it != vMasternodes.end()){ if((*it).vin == vin){ LogPrint("masternode", "CMasternodeMan: Removing Masternode %s - %i now\n", (*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) { mapSeenMasternodePing.insert(make_pair(mnb.lastPing.GetHash(), mnb.lastPing)); mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb)); masternodeSync.AddedMasternodeList(mnb.GetHash()); LogPrintf("CMasternodeMan::UpdateMasternodeList() - addr: %s\n vin: %s\n", mnb.addr.ToString(), mnb.vin.ToString()); CMasternode* pmn = Find(mnb.vin); if(pmn == NULL) { CMasternode mn(mnb); Add(mn); } else { pmn->UpdateFromNewBroadcast(mnb); } } bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, int& nDos) { nDos = 0; LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList - Masternode broadcast, vin: %s\n", mnb.vin.ToString()); if(mapSeenMasternodeBroadcast.count(mnb.GetHash())) { //seen masternodeSync.AddedMasternodeList(mnb.GetHash()); return true; } mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb)); LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList - Masternode broadcast, vin: %s new\n", mnb.vin.ToString()); if(!mnb.CheckAndUpdate(nDos)){ LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList - Masternode broadcast, vin: %s CheckAndUpdate failed\n", mnb.vin.ToString()); return false; } // make sure it's still unspent // - this is checked later by .check() in many places and by ThreadCheckDarkSendPool() if(mnb.CheckInputsAndAdd(nDos)) { masternodeSync.AddedMasternodeList(mnb.GetHash()); } else { LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList - Rejected Masternode entry %s\n", mnb.addr.ToString()); return false; } return true; }