dash/src/masternodeman.cpp

739 lines
24 KiB
C++
Raw Normal View History

2015-02-24 15:02:22 +01:00
// Copyright (c) 2014-2015 The Darkcoin 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 "core.h"
#include "util.h"
#include "addrman.h"
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
/** Masternode manager */
CMasternodeMan mnodeman;
struct CompareValueOnly
{
bool operator()(const pair<int64_t, CTxIn>& t1,
const pair<int64_t, CTxIn>& t2) const
{
return t1.first < t2.first;
}
};
//
// CMasternodeDB
//
CMasternodeDB::CMasternodeDB()
{
pathMN = GetDataDir() / "masternodes.dat";
}
bool CMasternodeDB::Write(const CMasternodeMan& mnodemanToSave)
{
// serialize addresses, checksum data up to that point, then append csum
CDataStream ssMasternodes(SER_DISK, CLIENT_VERSION);
ssMasternodes << FLATDATA(Params().MessageStart());
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();
return true;
}
bool CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad)
{
// 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)
return error("%s : Failed to open file %s", __func__, pathMN.string());
// 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<unsigned char> 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) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
}
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)
return error("%s : Checksum mismatch, data corrupted", __func__);
unsigned char pchMsgTmp[4];
try {
// de-serialize file header (network specific magic number) and ..
ssMasternodes >> FLATDATA(pchMsgTmp);
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
return error("%s : Invalid network magic number", __func__);
// de-serialize address data into one CMnList object
ssMasternodes >> mnodemanToLoad;
}
catch (std::exception &e) {
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
mnodemanToLoad.Clear();
}
return true;
}
void DumpMasternodes()
{
int64_t nStart = GetTimeMillis();
CMasternodeDB mndb;
mndb.Write(mnodeman);
LogPrintf("Flushed info to masternodes.dat %dms\n", GetTimeMillis() - nStart);
LogPrintf(" %s\n", mnodeman.ToString());
}
CMasternodeMan::CMasternodeMan() {}
bool CMasternodeMan::Add(CMasternode &mn)
{
LOCK(cs);
if (!mn.IsEnabled())
return false;
CMasternode *pmn = Find(mn.vin);
2015-02-25 12:54:03 +01:00
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<CMasternode>::iterator it = vMasternodes.begin();
while(it != vMasternodes.end()){
if((*it).activeState == 4 || (*it).activeState == 3){
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<CNetAddr, int64_t>::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<COutPoint, int64_t>::iterator it2 = mWeAskedForMasternodeListEntry.begin();
while(it2 != mWeAskedForMasternodeListEntry.end()){
if((*it2).second < GetTime()){
mWeAskedForMasternodeListEntry.erase(it2++);
} else {
++it2;
}
}
}
2015-03-01 01:04:17 +01:00
void CMasternodeMan::Clear()
{
LOCK(cs);
vMasternodes.clear();
mAskedUsForMasternodeList.clear();
mWeAskedForMasternodeList.clear();
mWeAskedForMasternodeListEntry.clear();
}
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<CNetAddr, int64_t>::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::FindOldestNotInVec(const std::vector<CTxIn> &vVins)
{
LOCK(cs);
CMasternode *pOldestMasternode = NULL;
BOOST_FOREACH(CMasternode &mn, vMasternodes)
{
mn.Check();
if(!mn.IsEnabled()) 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)
{
std::vector<pair<unsigned int, CTxIn> > vecMasternodeScores;
// 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.vin));
}
sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly());
2015-03-02 00:09:33 +01:00
int rank = 0;
BOOST_FOREACH (PAIRTYPE(unsigned int, CTxIn)& s, vecMasternodeScores){
rank++;
if(s.second == vin) {
return rank;
}
}
return -1;
}
2015-03-02 00:09:33 +01:00
CMasternode* CMasternodeMan::GetMasternodeByRank(int nRank, int64_t nBlockHeight, int minProtocol)
{
std::vector<pair<unsigned int, CTxIn> > vecMasternodeScores;
// 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.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()
{
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<unsigned char> vchSig, const int64_t nNow, const CPubKey pubkey, const CPubKey pubkey2, const int count, const int current, const int64_t lastUpdated, const int protocolVersion)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
pnode->PushMessage("dsee", vin, addr, vchSig, nNow, pubkey, pubkey2, count, current, lastUpdated, protocolVersion);
}
void CMasternodeMan::RelayMasternodeEntryPing(const CTxIn vin, const std::vector<unsigned char> 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);
if (strCommand == "dsee") { //DarkSend Election Entry
CTxIn vin;
CService addr;
CPubKey pubkey;
CPubKey pubkey2;
vector<unsigned char> vchSig;
int64_t sigTime;
int count;
int current;
int64_t lastUpdated;
int protocolVersion;
std::string strMessage;
// 70047 and greater
vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion;
// 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<std::string>(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast<std::string>(protocolVersion);
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;
}
//search existing masternode list, this is where we update existing masternodes with new dsee broadcasts
2015-02-25 12:54:03 +01:00
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
2015-02-25 12:54:03 +01:00
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());
2015-02-25 12:54:03 +01:00
pmn->pubkey2 = pubkey2;
pmn->sigTime = sigTime;
2015-02-25 12:54:03 +01:00
pmn->sig = vchSig;
pmn->protocolVersion = protocolVersion;
pmn->addr = addr;
pmn->Check();
if(pmn->IsEnabled())
2015-03-02 00:09:33 +01:00
mnodeman.RelayMasternodeEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion);
}
}
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 DRK tx got MASTERNODE_MIN_CONFIRMATIONS
uint256 hashBlock = 0;
GetTransaction(vin.prevout.hash, tx, hashBlock, true);
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
if (mi != mapBlockIndex.end() && (*mi).second)
{
CBlockIndex* pMNIndex = (*mi).second; // block for 1000 DRK 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);
// add our masternode
CMasternode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2, protocolVersion);
mn.UpdateLastSeen(lastUpdated);
this->Add(mn);
// if it matches our masternodeprivkey, then we've been remotely activated
if(pubkey2 == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION){
activeMasternode.EnableHotColdMasterNode(vin, addr);
}
if(count == -1 && !isLocal)
2015-03-02 00:09:33 +01:00
mnodeman.RelayMasternodeEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion);
} 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<unsigned char> 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
2015-02-25 12:54:03 +01:00
CMasternode* pmn = this->Find(vin);
if(pmn != NULL)
{
// LogPrintf("dseep - Found corresponding mn for vin: %s\n", vin.ToString().c_str());
// take this only if it's newer
2015-02-25 12:54:03 +01:00
if(pmn->lastDseep < sigTime)
{
2015-02-25 12:54:03 +01:00
std::string strMessage = pmn->addr.ToString() + boost::lexical_cast<std::string>(sigTime) + boost::lexical_cast<std::string>(stop);
std::string errorMessage = "";
2015-02-25 12:54:03 +01:00
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;
}
2015-02-25 12:54:03 +01:00
pmn->lastDseep = sigTime;
2015-02-25 12:54:03 +01:00
if(!pmn->UpdatedWithin(MASTERNODE_MIN_DSEEP_SECONDS))
{
2015-02-25 12:54:03 +01:00
if(stop) pmn->Disable();
else
{
2015-02-25 12:54:03 +01:00
pmn->UpdateLastSeen();
pmn->Check();
if(!pmn->IsEnabled()) return;
}
2015-03-02 00:09:33 +01:00
mnodeman.RelayMasternodeEntryPing(vin, vchSig, sigTime, stop);
}
}
return;
}
if(fDebug) LogPrintf("dseep - Couldn't find masternode entry %s\n", vin.ToString().c_str());
std::map<COutPoint, int64_t>::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 == "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<CNetAddr, int64_t>::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);
} 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);
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()
{
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();
return info.str();
}