// Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2019 The PIVX 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 "addrman.h" #include "masternode.h" #include "obfuscation.h" #include "spork.h" #include "util.h" #include #define MN_WINNER_MINIMUM_AGE 8000 // Age in seconds. This should be > MASTERNODE_REMOVAL_SECONDS to avoid misconfigured new nodes in the list. /** 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; } }; // // CMasternodeDB // CMasternodeDB::CMasternodeDB() { pathMN = GetDataDir() / "mncache.dat"; strMagicMessage = "MasternodeCache"; } bool CMasternodeDB::Write(const CMasternodeMan& mnodemanToSave) { int64_t nStart = GetTimeMillis(); // serialize, checksum data up to that point, then append checksum CDataStream ssMasternodes(SER_DISK, CLIENT_VERSION); ssMasternodes << strMagicMessage; // masternode cache file specific magic message ssMasternodes << FLATDATA(Params().MessageStart()); // network specific magic number ssMasternodes << mnodemanToSave; uint256 hash = Hash(ssMasternodes.begin(), ssMasternodes.end()); ssMasternodes << hash; // open output file, and associate with CAutoFile FILE* file = fopen(pathMN.string().c_str(), "wb"); CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) return error("%s : Failed to open file %s", __func__, pathMN.string()); // Write and commit header, data try { fileout << ssMasternodes; } catch (std::exception& e) { return error("%s : Serialize or I/O error - %s", __func__, e.what()); } // FileCommit(fileout); fileout.fclose(); LogPrint("masternode","Written info to mncache.dat %dms\n", GetTimeMillis() - nStart); LogPrint("masternode"," %s\n", mnodemanToSave.ToString()); return true; } CMasternodeDB::ReadResult CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad, bool fDryRun) { int64_t nStart = GetTimeMillis(); // open input file, and associate with CAutoFile FILE* file = fopen(pathMN.string().c_str(), "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { error("%s : Failed to open file %s", __func__, pathMN.string()); return FileError; } // use file size to size memory buffer int fileSize = boost::filesystem::file_size(pathMN); 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 ssMasternodes(vchData, SER_DISK, CLIENT_VERSION); // verify stored checksum matches input data uint256 hashTmp = Hash(ssMasternodes.begin(), ssMasternodes.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 .. ssMasternodes >> strMagicMessageTmp; // ... verify the message matches predefined one if (strMagicMessage != strMagicMessageTmp) { error("%s : Invalid masternode cache magic message", __func__); return IncorrectMagicMessage; } // de-serialize file header (network specific magic number) and .. ssMasternodes >> 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 CMasternodeMan object ssMasternodes >> mnodemanToLoad; } catch (std::exception& e) { mnodemanToLoad.Clear(); error("%s : Deserialize or I/O error - %s", __func__, e.what()); return IncorrectFormat; } LogPrint("masternode","Loaded info from mncache.dat %dms\n", GetTimeMillis() - nStart); LogPrint("masternode"," %s\n", mnodemanToLoad.ToString()); if (!fDryRun) { LogPrint("masternode","Masternode manager - cleaning....\n"); mnodemanToLoad.CheckAndRemove(true); LogPrint("masternode","Masternode manager - result:\n"); LogPrint("masternode"," %s\n", mnodemanToLoad.ToString()); } return Ok; } void DumpMasternodes() { int64_t nStart = GetTimeMillis(); CMasternodeDB mndb; CMasternodeMan tempMnodeman; LogPrint("masternode","Verifying mncache.dat format...\n"); CMasternodeDB::ReadResult readResult = mndb.Read(tempMnodeman, true); // there was an error and it was not an error on file opening => do not proceed if (readResult == CMasternodeDB::FileError) LogPrint("masternode","Missing masternode cache file - mncache.dat, will try to recreate\n"); else if (readResult != CMasternodeDB::Ok) { LogPrint("masternode","Error reading mncache.dat: "); if (readResult == CMasternodeDB::IncorrectFormat) LogPrint("masternode","magic is ok but data has invalid format, will try to recreate\n"); else { LogPrint("masternode","file format is unknown or invalid, please fix it manually\n"); return; } } LogPrint("masternode","Writting info to mncache.dat...\n"); mndb.Write(mnodeman); LogPrint("masternode","Masternode dump finished %dms\n", GetTimeMillis() - nStart); } CMasternodeMan::CMasternodeMan() { nDsqCount = 0; } bool CMasternodeMan::Add(CMasternode& mn) { LOCK(cs); if (!mn.IsEnabled()) return false; CMasternode* pmn = Find(mn.vin); if (pmn == NULL) { LogPrint("masternode", "CMasternodeMan: Adding new Masternode %s - %i now\n", mn.vin.prevout.hash.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 LogPrint("masternode", "CMasternodeMan::AskForMN - Asking node for missing entry, vin: %s\n", vin.prevout.hash.ToString()); pnode->PushMessage("dseg", vin); int64_t askAgain = GetTime() + MASTERNODE_MIN_MNP_SECONDS; mWeAskedForMasternodeListEntry[vin.prevout] = askAgain; } void CMasternodeMan::Check() { LOCK(cs); for (CMasternode& mn : vMasternodes) { mn.Check(); } } void CMasternodeMan::CheckAndRemove(bool forceExpiredRemoval) { 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) || (*it).protocolVersion < masternodePayments.GetMinMasternodePaymentsProto()) { LogPrint("masternode", "CMasternodeMan: Removing inactive Masternode %s - %i now\n", (*it).vin.prevout.hash.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)) { mapSeenMasternodeBroadcast.erase(it3++); masternodeSync.mapSeenSyncMNB.erase((*it3).second.GetHash()); } else { ++it3; } } // remove expired mapSeenMasternodePing map::iterator it4 = mapSeenMasternodePing.begin(); while (it4 != mapSeenMasternodePing.end()) { if ((*it4).second.sigTime < GetTime() - (MASTERNODE_REMOVAL_SECONDS * 2)) { 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::stable_size () { int nStable_size = 0; int nMinProtocol = ActiveProtocol(); int64_t nMasternode_Min_Age = MN_WINNER_MINIMUM_AGE; int64_t nMasternode_Age = 0; for (CMasternode& mn : vMasternodes) { if (mn.protocolVersion < nMinProtocol) { continue; // Skip obsolete versions } if (IsSporkActive (SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { nMasternode_Age = GetAdjustedTime() - mn.sigTime; if ((nMasternode_Age) < nMasternode_Min_Age) { continue; // Skip masternodes younger than (default) 8000 sec (MUST be > MASTERNODE_REMOVAL_SECONDS) } } mn.Check (); if (!mn.IsEnabled ()) continue; // Skip not-enabled masternodes nStable_size++; } return nStable_size; } int CMasternodeMan::CountEnabled(int protocolVersion) { int i = 0; protocolVersion = protocolVersion == -1 ? masternodePayments.GetMinMasternodePaymentsProto() : protocolVersion; for (CMasternode& mn : vMasternodes) { mn.Check(); if (mn.protocolVersion < protocolVersion || !mn.IsEnabled()) continue; i++; } return i; } void CMasternodeMan::CountNetworks(int protocolVersion, int& ipv4, int& ipv6, int& onion) { protocolVersion = protocolVersion == -1 ? masternodePayments.GetMinMasternodePaymentsProto() : protocolVersion; for (CMasternode& mn : vMasternodes) { mn.Check(); std::string strHost; int port; SplitHostPort(mn.addr.ToString(), port, strHost); CNetAddr node = CNetAddr(strHost, false); int nNetwork = node.GetNetwork(); switch (nNetwork) { case 1 : ipv4++; break; case 2 : ipv6++; break; case 3 : onion++; break; } } } void CMasternodeMan::DsegUpdate(CNode* pnode) { LOCK(cs); if (Params().NetworkID() == 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) { LogPrint("masternode", "dseg - we already asked peer %i for the list; skipping...\n", pnode->GetId()); return; } } } } pnode->PushMessage("dseg", CTxIn()); int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; mWeAskedForMasternodeList[pnode->addr] = askAgain; } CMasternode* CMasternodeMan::Find(const CScript& payee) { LOCK(cs); CScript payee2; for (CMasternode& mn : vMasternodes) { payee2 = GetScriptForDestination(mn.pubKeyCollateralAddress.GetID()); if (payee2 == payee) return &mn; } return NULL; } CMasternode* CMasternodeMan::Find(const CTxIn& vin) { LOCK(cs); for (CMasternode& mn : vMasternodes) { if (mn.vin.prevout == vin.prevout) return &mn; } return NULL; } CMasternode* CMasternodeMan::Find(const CPubKey& pubKeyMasternode) { LOCK(cs); for (CMasternode& mn : vMasternodes) { if (mn.pubKeyMasternode == 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(); for (CMasternode& mn : vMasternodes) { mn.Check(); if (!mn.IsEnabled()) continue; // //check protocol version if (mn.protocolVersion < masternodePayments.GetMinMasternodePaymentsProto()) continue; //it's in the list (up to 8 entries ahead of current block to allow propagation) -- so let's skip it if (masternodePayments.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; uint256 nHigh = 0; for (PAIRTYPE(int64_t, CTxIn) & s : vecMasternodeLastPaid) { CMasternode* pmn = Find(s.second); if (!pmn) break; uint256 n = pmn->CalculateScore(1, nBlockHeight - 100); if (n > nHigh) { nHigh = n; pBestMasternode = pmn; } nCountTenth++; if (nCountTenth >= nTenthNetwork) break; } return pBestMasternode; } CMasternode* CMasternodeMan::FindRandomNotInVec(std::vector& vecToExclude, int protocolVersion) { LOCK(cs); protocolVersion = protocolVersion == -1 ? masternodePayments.GetMinMasternodePaymentsProto() : protocolVersion; int nCountEnabled = CountEnabled(protocolVersion); LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec - nCountEnabled - vecToExclude.size() %d\n", nCountEnabled - vecToExclude.size()); if (nCountEnabled - vecToExclude.size() < 1) return NULL; int rand = GetRandInt(nCountEnabled - vecToExclude.size()); LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec - rand %d\n", rand); bool found; for (CMasternode& mn : vMasternodes) { if (mn.protocolVersion < protocolVersion || !mn.IsEnabled()) continue; found = false; for (CTxIn& usedVin : vecToExclude) { if (mn.vin.prevout == usedVin.prevout) { found = true; break; } } if (found) continue; if (--rand < 1) { return &mn; } } return NULL; } CMasternode* CMasternodeMan::GetCurrentMasterNode(int mod, int64_t nBlockHeight, int minProtocol) { int64_t score = 0; CMasternode* winner = NULL; // scan for winner for (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 = 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; int64_t nMasternode_Min_Age = MN_WINNER_MINIMUM_AGE; int64_t nMasternode_Age = 0; //make sure we know about this block uint256 hash = 0; if (!GetBlockHash(hash, nBlockHeight)) return -1; // scan for winner for (CMasternode& mn : vMasternodes) { if (mn.protocolVersion < minProtocol) { LogPrint("masternode","Skipping Masternode with obsolete version %d\n", mn.protocolVersion); continue; // Skip obsolete versions } if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { nMasternode_Age = GetAdjustedTime() - mn.sigTime; if ((nMasternode_Age) < nMasternode_Min_Age) { if (fDebug) LogPrint("masternode","Skipping just activated Masternode. Age: %ld\n", nMasternode_Age); continue; // Skip masternodes younger than (default) 1 hour } } if (fOnlyActive) { mn.Check(); if (!mn.IsEnabled()) continue; } uint256 n = mn.CalculateScore(1, nBlockHeight); int64_t n2 = n.GetCompact(false); vecMasternodeScores.push_back(make_pair(n2, mn.vin)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreTxIn()); int rank = 0; for (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 = 0; if (!GetBlockHash(hash, nBlockHeight)) return vecMasternodeRanks; // scan for winner for (CMasternode& mn : vMasternodes) { mn.Check(); if (mn.protocolVersion < minProtocol) continue; if (!mn.IsEnabled()) { vecMasternodeScores.push_back(make_pair(9999, mn)); continue; } uint256 n = mn.CalculateScore(1, nBlockHeight); int64_t n2 = n.GetCompact(false); vecMasternodeScores.push_back(make_pair(n2, mn)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreMN()); int rank = 0; for (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 for (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 = n.GetCompact(false); vecMasternodeScores.push_back(make_pair(n2, mn.vin)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareScoreTxIn()); int rank = 0; for (PAIRTYPE(int64_t, CTxIn) & s : vecMasternodeScores) { rank++; if (rank == nRank) { return Find(s.second); } } return NULL; } void CMasternodeMan::ProcessMasternodeConnections() { //we don't care about this for regtest if (Params().NetworkID() == CBaseChainParams::REGTEST) return; LOCK(cs_vNodes); for (CNode* pnode : vNodes) { if (pnode->fObfuScationMaster) { if (obfuScationPool.pSubmittedToMasternode != NULL && pnode->addr == obfuScationPool.pSubmittedToMasternode->addr) continue; LogPrint("masternode","Closing Masternode connection peer=%i \n", pnode->GetId()); pnode->fObfuScationMaster = false; pnode->Release(); } } } void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if (fLiteMode) return; //disable all Obfuscation/Masternode related functionality if (!masternodeSync.IsBlockchainSynced()) return; LOCK(cs_process_message); if (strCommand == "mnb") { //Masternode Broadcast CMasternodeBroadcast mnb; vRecv >> mnb; if (mapSeenMasternodeBroadcast.count(mnb.GetHash())) { //seen masternodeSync.AddedMasternodeList(mnb.GetHash()); return; } mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb)); int nDoS = 0; if (!mnb.CheckAndUpdate(nDoS)) { if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); //failed return; } // make sure the vout that was signed is related to the transaction that spawned the Masternode // - this is expensive, so it's only done once per Masternode if (!obfuScationSigner.IsVinAssociatedWithPubkey(mnb.vin, mnb.pubKeyCollateralAddress)) { LogPrintf("CMasternodeMan::ProcessMessage() : mnb - Got mismatched pubkey and vin\n"); Misbehaving(pfrom->GetId(), 33); return; } // make sure it's still unspent // - this is checked later by .check() in many places and by ThreadCheckObfuScationPool() if (mnb.CheckInputsAndAdd(nDoS)) { // use this as a peer addrman.Add(CAddress(mnb.addr), pfrom->addr, 2 * 60 * 60); masternodeSync.AddedMasternodeList(mnb.GetHash()); } else { LogPrint("masternode","mnb - Rejected Masternode entry %s\n", mnb.vin.prevout.hash.ToString()); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } } else if (strCommand == "mnp") { //Masternode Ping CMasternodePing mnp; vRecv >> mnp; LogPrint("masternode", "mnp - Masternode ping, vin: %s\n", mnp.vin.prevout.hash.ToString()); if (mapSeenMasternodePing.count(mnp.GetHash())) return; //seen mapSeenMasternodePing.insert(make_pair(mnp.GetHash(), mnp)); 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 == "dseg") { //Get Masternode list or specific entry CTxIn vin; vRecv >> vin; if (vin == CTxIn()) { //only should ask for this once //local network bool isLocal = (pfrom->addr.IsRFC1918() || pfrom->addr.IsLocal()); if (!isLocal && Params().NetworkID() == CBaseChainParams::MAIN) { std::map::iterator i = mAskedUsForMasternodeList.find(pfrom->addr); if (i != mAskedUsForMasternodeList.end()) { int64_t t = (*i).second; if (GetTime() < t) { LogPrintf("CMasternodeMan::ProcessMessage() : dseg - peer already asked me for the list\n"); Misbehaving(pfrom->GetId(), 34); return; } } int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; mAskedUsForMasternodeList[pfrom->addr] = askAgain; } } //else, asking for a specific node which is ok int nInvCount = 0; for (CMasternode& mn : vMasternodes) { if (mn.addr.IsRFC1918()) continue; //local network if (mn.IsEnabled()) { LogPrint("masternode", "dseg - Sending Masternode entry - %s \n", mn.vin.prevout.hash.ToString()); if (vin == CTxIn() || vin == mn.vin) { 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) { LogPrint("masternode", "dseg - Sent 1 Masternode entry to peer %i\n", pfrom->GetId()); return; } } } } if (vin == CTxIn()) { pfrom->PushMessage("ssc", MASTERNODE_SYNC_LIST, nInvCount); LogPrint("masternode", "dseg - Sent %d Masternode entries to peer %i\n", nInvCount, pfrom->GetId()); } } /* * IT'S SAFE TO REMOVE THIS IN FURTHER VERSIONS * AFTER MIGRATION TO V12 IS DONE */ // Light version for OLD MASSTERNODES - fake pings, no self-activation else if (strCommand == "dsee") { //ObfuScation Election Entry if (IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return; CTxIn vin; CService addr; CPubKey pubkey; CPubKey pubkey2; vector vchSig; int64_t sigTime; int count; int current; int64_t lastUpdated; int protocolVersion; CScript donationAddress; int donationPercentage; std::string strMessage; vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion >> donationAddress >> donationPercentage; // make sure signature isn't in the future (past is OK) if (sigTime > GetAdjustedTime() + 60 * 60) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString()); Misbehaving(pfrom->GetId(), 1); return; } std::string vchPubKey(pubkey.begin(), pubkey.end()); std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); strMessage = addr.ToString() + std::to_string(sigTime) + vchPubKey + vchPubKey2 + std::to_string(protocolVersion) + donationAddress.ToString() + std::to_string(donationPercentage); if (protocolVersion < masternodePayments.GetMinMasternodePaymentsProto()) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - ignoring outdated Masternode %s protocol version %d < %d\n", vin.prevout.hash.ToString(), protocolVersion, masternodePayments.GetMinMasternodePaymentsProto()); Misbehaving(pfrom->GetId(), 1); return; } CScript pubkeyScript; pubkeyScript = GetScriptForDestination(pubkey.GetID()); if (pubkeyScript.size() != 25) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - pubkey the wrong size\n"); Misbehaving(pfrom->GetId(), 100); return; } CScript pubkeyScript2; pubkeyScript2 = GetScriptForDestination(pubkey2.GetID()); if (pubkeyScript2.size() != 25) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - pubkey2 the wrong size\n"); Misbehaving(pfrom->GetId(), 100); return; } if (!vin.scriptSig.empty()) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - Ignore Not Empty ScriptSig %s\n", vin.prevout.hash.ToString()); Misbehaving(pfrom->GetId(), 100); return; } std::string errorMessage = ""; if (!obfuScationSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - Got bad Masternode address signature\n"); Misbehaving(pfrom->GetId(), 100); return; } if (Params().NetworkID() == CBaseChainParams::MAIN) { if (addr.GetPort() != 51336) return; } else if (addr.GetPort() == 51336) return; //search existing Masternode list, this is where we update existing Masternodes with new dsee broadcasts CMasternode* pmn = this->Find(vin); if (pmn != NULL) { // count == -1 when it's a new entry // e.g. We don't want the entry relayed/time updated when we're syncing the list // mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below, // after that they just need to match if (count == -1 && pmn->pubKeyCollateralAddress == pubkey && (GetAdjustedTime() - pmn->nLastDsee > MASTERNODE_MIN_MNB_SECONDS)) { if (pmn->protocolVersion > GETHEADERS_VERSION && sigTime - pmn->lastPing.sigTime < MASTERNODE_MIN_MNB_SECONDS) return; if (pmn->nLastDsee < sigTime) { //take the newest entry LogPrint("masternode", "dsee - Got updated entry for %s\n", vin.prevout.hash.ToString()); if (pmn->protocolVersion < GETHEADERS_VERSION) { pmn->pubKeyMasternode = pubkey2; pmn->sigTime = sigTime; pmn->sig = vchSig; pmn->protocolVersion = protocolVersion; pmn->addr = addr; //fake ping pmn->lastPing = CMasternodePing(vin); } pmn->nLastDsee = sigTime; pmn->Check(); if (pmn->IsEnabled()) { TRY_LOCK(cs_vNodes, lockNodes); if (!lockNodes) return; for (CNode* pnode : vNodes) if (pnode->nVersion >= masternodePayments.GetMinMasternodePaymentsProto()) pnode->PushMessage("dsee", vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion, donationAddress, donationPercentage); } } } return; } static std::map mapSeenDsee; if (mapSeenDsee.count(vin.prevout) && mapSeenDsee[vin.prevout] == pubkey) { LogPrint("masternode", "dsee - already seen this vin %s\n", vin.prevout.ToString()); return; } mapSeenDsee.insert(make_pair(vin.prevout, pubkey)); // make sure the vout that was signed is related to the transaction that spawned the Masternode // - this is expensive, so it's only done once per Masternode if (!obfuScationSigner.IsVinAssociatedWithPubkey(vin, pubkey)) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - Got mismatched pubkey and vin\n"); Misbehaving(pfrom->GetId(), 100); return; } LogPrint("masternode", "dsee - Got NEW OLD Masternode entry %s\n", vin.prevout.hash.ToString()); // make sure it's still unspent // - this is checked later by .check() in many places and by ThreadCheckObfuScationPool() CValidationState state; CMutableTransaction tx = CMutableTransaction(); CTxOut vout = CTxOut(9999.99 * COIN, obfuScationPool.collateralPubKey); tx.vin.push_back(vin); tx.vout.push_back(vout); bool fAcceptable = false; { TRY_LOCK(cs_main, lockMain); if (!lockMain) return; fAcceptable = AcceptableInputs(mempool, state, CTransaction(tx), false, NULL); } if (fAcceptable) { if (GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS) { LogPrintf("CMasternodeMan::ProcessMessage() : dsee - Input must have least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); Misbehaving(pfrom->GetId(), 20); return; } // verify that sig time is legit in past // should be at least not earlier than block when 1000 Agrarian tx got MASTERNODE_MIN_CONFIRMATIONS uint256 hashBlock = 0; CTransaction tx2; GetTransaction(vin.prevout.hash, tx2, hashBlock, true); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pMNIndex = (*mi).second; // block for 10000 AGR tx -> 1 confirmation CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + MASTERNODE_MIN_CONFIRMATIONS - 1]; // block where tx got MASTERNODE_MIN_CONFIRMATIONS if (pConfIndex->GetBlockTime() > sigTime) { LogPrint("masternode","mnb - Bad sigTime %d for Masternode %s (%i conf block is at %d)\n", sigTime, vin.prevout.hash.ToString(), MASTERNODE_MIN_CONFIRMATIONS, pConfIndex->GetBlockTime()); return; } } // use this as a peer addrman.Add(CAddress(addr), pfrom->addr, 2 * 60 * 60); // add Masternode CMasternode mn = CMasternode(); mn.addr = addr; mn.vin = vin; mn.pubKeyCollateralAddress = pubkey; mn.sig = vchSig; mn.sigTime = sigTime; mn.pubKeyMasternode = pubkey2; mn.protocolVersion = protocolVersion; // fake ping mn.lastPing = CMasternodePing(vin); mn.Check(true); // add v11 masternodes, v12 should be added by mnb only if (protocolVersion < GETHEADERS_VERSION) { LogPrint("masternode", "dsee - Accepted OLD Masternode entry %i %i\n", count, current); Add(mn); } if (mn.IsEnabled()) { TRY_LOCK(cs_vNodes, lockNodes); if (!lockNodes) return; for (CNode* pnode : vNodes) if (pnode->nVersion >= masternodePayments.GetMinMasternodePaymentsProto()) pnode->PushMessage("dsee", vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion, donationAddress, donationPercentage); } } else { LogPrint("masternode","dsee - Rejected Masternode entry %s\n", vin.prevout.hash.ToString()); int nDoS = 0; if (state.IsInvalid(nDoS)) { LogPrint("masternode","dsee - %s from %i %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), pfrom->GetId(), pfrom->cleanSubVer.c_str()); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } } } else if (strCommand == "dseep") { //ObfuScation Election Entry Ping if (IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return; CTxIn vin; vector vchSig; int64_t sigTime; bool stop; vRecv >> vin >> vchSig >> sigTime >> stop; //LogPrint("masternode","dseep - Received: vin: %s sigTime: %lld stop: %s\n", vin.ToString().c_str(), sigTime, stop ? "true" : "false"); if (sigTime > GetAdjustedTime() + 60 * 60) { LogPrintf("CMasternodeMan::ProcessMessage() : dseep - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString()); Misbehaving(pfrom->GetId(), 1); return; } if (sigTime <= GetAdjustedTime() - 60 * 60) { LogPrintf("CMasternodeMan::ProcessMessage() : dseep - Signature rejected, too far into the past %s - %d %d \n", vin.prevout.hash.ToString(), sigTime, GetAdjustedTime()); Misbehaving(pfrom->GetId(), 1); return; } 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 } // see if we have this Masternode CMasternode* pmn = this->Find(vin); if (pmn != NULL && pmn->protocolVersion >= masternodePayments.GetMinMasternodePaymentsProto()) { // LogPrint("masternode","dseep - Found corresponding mn for vin: %s\n", vin.ToString().c_str()); // take this only if it's newer if (sigTime - pmn->nLastDseep > MASTERNODE_MIN_MNP_SECONDS) { std::string strMessage = pmn->addr.ToString() + std::to_string(sigTime) + std::to_string(stop); std::string errorMessage = ""; if (!obfuScationSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, errorMessage)) { LogPrint("masternode","dseep - Got bad Masternode address signature %s \n", vin.prevout.hash.ToString()); //Misbehaving(pfrom->GetId(), 100); return; } // fake ping for v11 masternodes, ignore for v12 if (pmn->protocolVersion < GETHEADERS_VERSION) pmn->lastPing = CMasternodePing(vin); pmn->nLastDseep = sigTime; pmn->Check(); if (pmn->IsEnabled()) { TRY_LOCK(cs_vNodes, lockNodes); if (!lockNodes) return; LogPrint("masternode", "dseep - relaying %s \n", vin.prevout.hash.ToString()); for (CNode* pnode : vNodes) if (pnode->nVersion >= masternodePayments.GetMinMasternodePaymentsProto()) pnode->PushMessage("dseep", vin, vchSig, sigTime, stop); } } return; } LogPrint("masternode", "dseep - Couldn't find Masternode entry %s peer=%i\n", vin.prevout.hash.ToString(), pfrom->GetId()); AskForMN(pfrom, vin); } /* * END OF "REMOVE" */ } 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).vin.prevout.hash.ToString(), size() - 1); vMasternodes.erase(it); break; } ++it; } } 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()); LogPrint("masternode","CMasternodeMan::UpdateMasternodeList() -- masternode=%s\n", mnb.vin.prevout.ToString()); CMasternode* pmn = Find(mnb.vin); if (pmn == NULL) { CMasternode mn(mnb); Add(mn); } else { pmn->UpdateFromNewBroadcast(mnb); } } 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(); }