// Copyright (c) 2014-2015 The Dash developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "masternodeman.h" #include "masternode.h" #include "activemasternode.h" #include "darksend.h" #include "core.h" #include "util.h" #include "addrman.h" #include #include CCriticalSection cs_process_message; /** Masternode manager */ CMasternodeMan mnodeman; struct CompareValueOnly { bool operator()(const pair& t1, const pair& t2) const { return t1.first < t2.first; } }; struct CompareValueOnlyMN { 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 = CAutoFile(file, SER_DISK, CLIENT_VERSION); if (!fileout) 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(); LogPrintf("Written info to mncache.dat %dms\n", GetTimeMillis() - nStart); LogPrintf(" %s\n", mnodemanToSave.ToString()); return true; } CMasternodeDB::ReadResult CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad) { int64_t nStart = GetTimeMillis(); // open input file, and associate with CAutoFile FILE *file = fopen(pathMN.string().c_str(), "rb"); CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); if (!filein) { 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; } mnodemanToLoad.CheckAndRemove(); // clean out expired LogPrintf("Loaded info from mncache.dat %dms\n", GetTimeMillis() - nStart); LogPrintf(" %s\n", mnodemanToLoad.ToString()); return Ok; } void DumpMasternodes() { int64_t nStart = GetTimeMillis(); CMasternodeDB mndb; CMasternodeMan tempMnodeman; LogPrintf("Verifying mncache.dat format...\n"); CMasternodeDB::ReadResult readResult = mndb.Read(tempMnodeman); // there was an error and it was not an error on file openning => do not proceed if (readResult == CMasternodeDB::FileError) LogPrintf("Missing masternode cache file - mncache.dat, will try to recreate\n"); else if (readResult != CMasternodeDB::Ok) { LogPrintf("Error reading mncache.dat: "); if(readResult == CMasternodeDB::IncorrectFormat) LogPrintf("magic is ok but data has invalid format, will try to recreate\n"); else { LogPrintf("file format is unknown or invalid, please fix it manually\n"); return; } } LogPrintf("Writting info to mncache.dat...\n"); mndb.Write(mnodeman); LogPrintf("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) { if(fDebug) LogPrintf("CMasternodeMan: Adding new Masternode %s - %i now\n", mn.addr.ToString().c_str(), size() + 1); vMasternodes.push_back(mn); return true; } return false; } void CMasternodeMan::Check() { LOCK(cs); BOOST_FOREACH(CMasternode& mn, vMasternodes) mn.Check(); } void CMasternodeMan::CheckAndRemove() { LOCK(cs); Check(); //remove inactive vector::iterator it = vMasternodes.begin(); while(it != vMasternodes.end()){ if((*it).activeState == CMasternode::MASTERNODE_REMOVE || (*it).activeState == CMasternode::MASTERNODE_VIN_SPENT){ if(fDebug) LogPrintf("CMasternodeMan: Removing inactive Masternode %s - %i now\n", (*it).addr.ToString().c_str(), size() - 1); 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; } } } void CMasternodeMan::Clear() { LOCK(cs); vMasternodes.clear(); mAskedUsForMasternodeList.clear(); mWeAskedForMasternodeList.clear(); mWeAskedForMasternodeListEntry.clear(); nDsqCount = 0; } int CMasternodeMan::CountEnabled() { int i = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.IsEnabled()) i++; } return i; } int CMasternodeMan::CountMasternodesAboveProtocol(int protocolVersion) { int i = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.protocolVersion < protocolVersion || !mn.IsEnabled()) continue; i++; } return i; } void CMasternodeMan::DsegUpdate(CNode* pnode) { LOCK(cs); 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("dseg", CTxIn()); int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; mWeAskedForMasternodeList[pnode->addr] = askAgain; } CMasternode *CMasternodeMan::Find(const CTxIn &vin) { LOCK(cs); BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.vin == vin) 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; } CMasternode* CMasternodeMan::FindOldestNotInVec(const std::vector &vVins, int nMinimumAge, int nMinimumActiveSeconds) { LOCK(cs); CMasternode *pOldestMasternode = NULL; BOOST_FOREACH(CMasternode &mn, vMasternodes) { mn.Check(); if(!mn.IsEnabled()) continue; if(!RegTest()){ if(mn.GetMasternodeInputAge() < nMinimumAge || mn.lastTimeSeen - mn.sigTime < nMinimumActiveSeconds) continue; } bool found = false; BOOST_FOREACH(const CTxIn& vin, vVins) if(mn.vin == vin) { found = true; break; } if(found) continue; if(pOldestMasternode == NULL || pOldestMasternode->GetMasternodeInputAge() < mn.GetMasternodeInputAge()){ pOldestMasternode = &mn; } } return pOldestMasternode; } CMasternode *CMasternodeMan::FindRandom() { LOCK(cs); if(size() == 0) return NULL; return &vMasternodes[GetRandInt(vMasternodes.size())]; } CMasternode* CMasternodeMan::GetCurrentMasterNode(int mod, int64_t nBlockHeight, int minProtocol) { unsigned int 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); unsigned int n2 = 0; memcpy(&n2, &n, sizeof(n2)); // 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 = 0; 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); unsigned int n2 = 0; memcpy(&n2, &n, sizeof(n2)); vecMasternodeScores.push_back(make_pair(n2, mn.vin)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly()); int rank = 0; BOOST_FOREACH (PAIRTYPE(unsigned int, CTxIn)& s, vecMasternodeScores){ rank++; if(s.second == vin) { 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 BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); if(mn.protocolVersion < minProtocol) continue; if(!mn.IsEnabled()) { continue; } uint256 n = mn.CalculateScore(1, nBlockHeight); unsigned int n2 = 0; memcpy(&n2, &n, sizeof(n2)); vecMasternodeScores.push_back(make_pair(n2, mn)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnlyMN()); int rank = 0; BOOST_FOREACH (PAIRTYPE(unsigned int, 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); unsigned int n2 = 0; memcpy(&n2, &n, sizeof(n2)); vecMasternodeScores.push_back(make_pair(n2, mn.vin)); } sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly()); int rank = 0; BOOST_FOREACH (PAIRTYPE(unsigned int, 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(RegTest()) return; LOCK(cs_vNodes); if(!darkSendPool.pSubmittedToMasternode) return; BOOST_FOREACH(CNode* pnode, vNodes) { if(darkSendPool.pSubmittedToMasternode->addr == pnode->addr) continue; if(pnode->fDarkSendMaster){ LogPrintf("Closing Masternode connection %s \n", pnode->addr.ToString().c_str()); pnode->CloseSocketDisconnect(); } } } void CMasternodeMan::RelayMasternodeEntry(const CTxIn vin, const CService addr, const std::vector vchSig, const int64_t nNow, const CPubKey pubkey, const CPubKey pubkey2, const int count, const int current, const int64_t lastUpdated, const int protocolVersion, CScript donationAddress, int donationPercentage) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) pnode->PushMessage("dsee", vin, addr, vchSig, nNow, pubkey, pubkey2, count, current, lastUpdated, protocolVersion, donationAddress, donationPercentage); } void CMasternodeMan::RelayMasternodeEntryPing(const CTxIn vin, const std::vector vchSig, const int64_t nNow, const bool stop) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) pnode->PushMessage("dseep", vin, vchSig, nNow, stop); } void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if(fLiteMode) return; //disable all Darksend/Masternode related functionality if(IsInitialBlockDownload()) return; LOCK(cs_process_message); if (strCommand == "dsee") { //DarkSend Election Entry 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; // 70047 and greater 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("dsee - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); return; } bool isLocal = addr.IsRFC1918() || addr.IsLocal(); if(RegTest()) isLocal = false; std::string vchPubKey(pubkey.begin(), pubkey.end()); std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); strMessage = addr.ToString() + boost::lexical_cast(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(protocolVersion) + donationAddress.ToString() + boost::lexical_cast(donationPercentage); if(donationPercentage < 0 || donationPercentage > 100){ LogPrintf("dsee - donation percentage out of range %d\n", donationPercentage); return; } if(protocolVersion < nMasternodeMinProtocol) { LogPrintf("dsee - ignoring outdated Masternode %s protocol version %d\n", vin.ToString().c_str(), protocolVersion); return; } CScript pubkeyScript; pubkeyScript.SetDestination(pubkey.GetID()); if(pubkeyScript.size() != 25) { LogPrintf("dsee - pubkey the wrong size\n"); Misbehaving(pfrom->GetId(), 100); return; } CScript pubkeyScript2; pubkeyScript2.SetDestination(pubkey2.GetID()); if(pubkeyScript2.size() != 25) { LogPrintf("dsee - pubkey2 the wrong size\n"); Misbehaving(pfrom->GetId(), 100); return; } std::string errorMessage = ""; if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)){ LogPrintf("dsee - Got bad Masternode address signature\n"); Misbehaving(pfrom->GetId(), 100); return; } if(Params().NetworkID() == CChainParams::MAIN){ if(addr.GetPort() != 9999) return; } else if(addr.GetPort() == 9999) return; //search existing Masternode list, this is where we update existing Masternodes with new dsee broadcasts CMasternode* pmn = this->Find(vin); // if we are masternode but with undefined vin and this dsee is ours (matches our Masternode privkey) then just skip this part if(pmn != NULL && !(fMasterNode && activeMasternode.vin == CTxIn() && pubkey2 == activeMasternode.pubKeyMasternode)) { // 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->pubkey == pubkey && !pmn->UpdatedWithin(MASTERNODE_MIN_DSEE_SECONDS)){ pmn->UpdateLastSeen(); if(pmn->sigTime < sigTime){ //take the newest entry LogPrintf("dsee - Got updated entry for %s\n", addr.ToString().c_str()); pmn->pubkey2 = pubkey2; pmn->sigTime = sigTime; pmn->sig = vchSig; pmn->protocolVersion = protocolVersion; pmn->addr = addr; pmn->donationAddress = donationAddress; pmn->donationPercentage = donationPercentage; pmn->Check(); if(pmn->IsEnabled()) mnodeman.RelayMasternodeEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion, donationAddress, donationPercentage); } } 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(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubkey)) { LogPrintf("dsee - Got mismatched pubkey and vin\n"); Misbehaving(pfrom->GetId(), 100); return; } if(fDebug) LogPrintf("dsee - Got NEW Masternode entry %s\n", addr.ToString().c_str()); // make sure it's still unspent // - this is checked later by .check() in many places and by ThreadCheckDarkSendPool() CValidationState state; CTransaction tx = CTransaction(); CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey); tx.vin.push_back(vin); tx.vout.push_back(vout); if(AcceptableInputs(mempool, state, tx)){ if(fDebug) LogPrintf("dsee - Accepted Masternode entry %i %i\n", count, current); if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ LogPrintf("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 DASH tx got MASTERNODE_MIN_CONFIRMATIONS uint256 hashBlock = 0; GetTransaction(vin.prevout.hash, tx, hashBlock, true); map::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pMNIndex = (*mi).second; // block for 1000 DASH tx -> 1 confirmation CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + MASTERNODE_MIN_CONFIRMATIONS - 1]; // block where tx got MASTERNODE_MIN_CONFIRMATIONS if(pConfIndex->GetBlockTime() > sigTime) { LogPrintf("dsee - Bad sigTime %d for Masternode %20s %105s (%i conf block is at %d)\n", sigTime, addr.ToString(), vin.ToString(), MASTERNODE_MIN_CONFIRMATIONS, pConfIndex->GetBlockTime()); return; } } // use this as a peer addrman.Add(CAddress(addr), pfrom->addr, 2*60*60); //doesn't support multisig addresses if(donationAddress.IsPayToScriptHash()){ donationAddress = CScript(); donationPercentage = 0; } // add our Masternode CMasternode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2, protocolVersion, donationAddress, donationPercentage); mn.UpdateLastSeen(lastUpdated); this->Add(mn); // if it matches our Masternode privkey, then we've been remotely activated if(pubkey2 == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION){ activeMasternode.EnableHotColdMasterNode(vin, addr); } if(count == -1 && !isLocal) mnodeman.RelayMasternodeEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion, donationAddress, donationPercentage); } else { LogPrintf("dsee - Rejected Masternode entry %s\n", addr.ToString().c_str()); int nDoS = 0; if (state.IsInvalid(nDoS)) { LogPrintf("dsee - %s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } } } else if (strCommand == "dseep") { //DarkSend Election Entry Ping CTxIn vin; vector vchSig; int64_t sigTime; bool stop; vRecv >> vin >> vchSig >> sigTime >> stop; //LogPrintf("dseep - Received: vin: %s sigTime: %lld stop: %s\n", vin.ToString().c_str(), sigTime, stop ? "true" : "false"); if (sigTime > GetAdjustedTime() + 60 * 60) { LogPrintf("dseep - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); return; } if (sigTime <= GetAdjustedTime() - 60 * 60) { LogPrintf("dseep - Signature rejected, too far into the past %s - %d %d \n", vin.ToString().c_str(), sigTime, GetAdjustedTime()); return; } // see if we have this Masternode CMasternode* pmn = this->Find(vin); if(pmn != NULL && pmn->protocolVersion >= nMasternodeMinProtocol) { // LogPrintf("dseep - Found corresponding mn for vin: %s\n", vin.ToString().c_str()); // take this only if it's newer if(pmn->lastDseep < sigTime) { std::string strMessage = pmn->addr.ToString() + boost::lexical_cast(sigTime) + boost::lexical_cast(stop); std::string errorMessage = ""; if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) { LogPrintf("dseep - Got bad Masternode address signature %s \n", vin.ToString().c_str()); //Misbehaving(pfrom->GetId(), 100); return; } pmn->lastDseep = sigTime; if(!pmn->UpdatedWithin(MASTERNODE_MIN_DSEEP_SECONDS)) { if(stop) pmn->Disable(); else { pmn->UpdateLastSeen(); pmn->Check(); if(!pmn->IsEnabled()) return; } mnodeman.RelayMasternodeEntryPing(vin, vchSig, sigTime, stop); } } return; } if(fDebug) LogPrintf("dseep - Couldn't find Masternode entry %s\n", vin.ToString().c_str()); 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 dsee info once from the node that sent dseep LogPrintf("dseep - Asking source node for missing entry %s\n", vin.ToString().c_str()); pfrom->PushMessage("dseg", vin); int64_t askAgain = GetTime() + MASTERNODE_MIN_DSEEP_SECONDS; mWeAskedForMasternodeListEntry[vin.prevout] = askAgain; } else if (strCommand == "mvote") { //Masternode Vote CTxIn vin; vector vchSig; int nVote; vRecv >> vin >> vchSig >> nVote; // see if we have this Masternode CMasternode* pmn = this->Find(vin); if(pmn != NULL) { if((GetAdjustedTime() - pmn->lastVote) > (60*60)) { std::string strMessage = vin.ToString() + boost::lexical_cast(nVote); std::string errorMessage = ""; if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) { LogPrintf("mvote - Got bad Masternode address signature %s \n", vin.ToString().c_str()); return; } pmn->nVote = nVote; pmn->lastVote = GetAdjustedTime(); //send to all peers LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) pnode->PushMessage("mvote", vin, vchSig, nVote); } return; } } 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 if(!pfrom->addr.IsRFC1918() && Params().NetworkID() == CChainParams::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 count = this->size(); int i = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) { if(mn.addr.IsRFC1918()) continue; //local network if(mn.IsEnabled()) { if(fDebug) LogPrintf("dseg - Sending Masternode entry - %s \n", mn.addr.ToString().c_str()); if(vin == CTxIn()){ pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.sigTime, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion, mn.donationAddress, mn.donationPercentage); } else if (vin == mn.vin) { pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.sigTime, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion, mn.donationAddress, mn.donationPercentage); LogPrintf("dseg - Sent 1 Masternode entries to %s\n", pfrom->addr.ToString().c_str()); return; } i++; } } LogPrintf("dseg - Sent %d Masternode entries to %s\n", i, pfrom->addr.ToString().c_str()); } } 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(); }