dash/src/masternodeman.cpp

736 lines
22 KiB
C++
Raw Normal View History

2015-03-18 00:06:58 +01:00
// Copyright (c) 2014-2015 The Dash developers
2015-02-24 15:02:22 +01:00
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "masternodeman.h"
2015-03-23 13:59:22 +01:00
#include "masternode.h"
#include "activemasternode.h"
#include "darksend.h"
#include "util.h"
#include "addrman.h"
#include "spork.h"
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
2015-03-31 23:21:59 +02:00
CCriticalSection cs_process_message;
/** Masternode manager */
CMasternodeMan mnodeman;
// Keep track of all broadcasts I've seen
map<uint256, CMasternodeBroadcast> mapSeenMasternodeBroadcast;
// Keep track of all pings I've seen
map<uint256, CMasternodePing> mapSeenMasternodePing;
struct CompareValueOnly
{
bool operator()(const pair<int64_t, CTxIn>& t1,
const pair<int64_t, CTxIn>& t2) const
{
return t1.first < t2.first;
}
};
struct CompareValueOnlyMN
{
bool operator()(const pair<int64_t, CMasternode>& t1,
const pair<int64_t, CMasternode>& 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");
2015-04-03 00:51:08 +02:00
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());
}
2015-04-03 00:51:08 +02:00
// 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");
2015-04-03 00:51:08 +02:00
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<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) {
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);
}
2015-03-06 18:25:48 +01:00
CMasternodeMan::CMasternodeMan() {
nDsqCount = 0;
}
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)
{
2015-03-05 09:10:15 +01:00
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);
2015-06-24 18:41:03 +02:00
BOOST_FOREACH(CMasternode& mn, vMasternodes) {
mn.Check();
2015-06-24 18:41:03 +02:00
// // if it matches our Masternode privkey, then we've been remotely activated
// if(mn.pubkey2 == activeMasternode.pubKeyMasternode && mn.protocolVersion == PROTOCOL_VERSION){
// activeMasternode.EnableHotColdMasterNode(mn.vin, mn.addr);
// }
}
}
void CMasternodeMan::CheckAndRemove()
{
LOCK(cs);
Check();
//remove inactive
vector<CMasternode>::iterator it = vMasternodes.begin();
while(it != vMasternodes.end()){
if((*it).activeState == CMasternode::MASTERNODE_REMOVE || (*it).activeState == CMasternode::MASTERNODE_VIN_SPENT || (*it).protocolVersion < nMasternodeMinProtocol){
2015-03-05 09:10:15 +01:00
if(fDebug) LogPrintf("CMasternodeMan: Removing inactive Masternode %s - %i now\n", (*it).addr.ToString().c_str(), size() - 1);
it = vMasternodes.erase(it);
} else {
++it;
}
}
2015-03-05 09:10:15 +01:00
// 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;
}
}
2015-03-05 09:10:15 +01:00
// check who we asked for the Masternode list
it1 = mWeAskedForMasternodeList.begin();
while(it1 != mWeAskedForMasternodeList.end()){
if((*it1).second < GetTime()){
mWeAskedForMasternodeList.erase(it1++);
} else {
++it1;
}
}
2015-03-05 09:10:15 +01:00
// 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();
2015-03-06 18:25:48 +01:00
nDsqCount = 0;
2015-03-01 01:04:17 +01:00
}
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 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)
{
2015-04-09 21:17:32 +02:00
if(mn.vin.prevout == vin.prevout)
return &mn;
}
return NULL;
}
2015-04-07 21:59:30 +02:00
CMasternode *CMasternodeMan::Find(const CPubKey &pubKeyMasternode)
{
LOCK(cs);
BOOST_FOREACH(CMasternode& mn, vMasternodes)
{
if(mn.pubkey2 == pubKeyMasternode)
return &mn;
}
return NULL;
}
CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight)
{
LOCK(cs);
CMasternode *pOldestMasternode = NULL;
BOOST_FOREACH(CMasternode &mn, vMasternodes)
{
mn.Check();
if(!mn.IsEnabled()) continue;
// //check protocol version
if(IsSporkActive(SPORK_10_MASTERNODE_PAY_NEWEST_NODES)){
if(mn.protocolVersion < MIN_MASTERNODE_PAYMENT_PROTO_VERSION_2) continue;
} else {
//support older versions for a period of time
if(mn.protocolVersion < MIN_MASTERNODE_PAYMENT_PROTO_VERSION_1) continue;
}
//it's in the list -- so let's skip it
if(masternodePayments.IsScheduled(mn, nBlockHeight)) continue;
//make sure it has as many confirmations as there are masternodes
if(mn.GetMasternodeInputAge() < CountEnabled()) continue;
if(pOldestMasternode == NULL || pOldestMasternode->SecondsSincePayment() < mn.SecondsSincePayment()){
pOldestMasternode = &mn;
2015-03-16 21:57:07 +01:00
}
}
return pOldestMasternode;
}
CMasternode *CMasternodeMan::FindRandom()
{
LOCK(cs);
if(size() == 0) return NULL;
return &vMasternodes[GetRandInt(vMasternodes.size())];
}
void CMasternodeMan::DecrementVotedTimes()
{
BOOST_FOREACH(CMasternode& mn, vMasternodes)
if(--mn.nVotedTimes < 0) mn.nVotedTimes = 0;
}
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;
2015-03-05 09:10:15 +01:00
// 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<pair<unsigned int, CTxIn> > vecMasternodeScores;
2015-03-23 13:59:22 +01:00
//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());
2015-03-02 00:09:33 +01:00
int rank = 0;
BOOST_FOREACH (PAIRTYPE(unsigned int, CTxIn)& s, vecMasternodeScores){
rank++;
if(s.second.prevout == vin.prevout) {
return rank;
}
}
return -1;
}
std::vector<pair<int, CMasternode> > CMasternodeMan::GetMasternodeRanks(int64_t nBlockHeight, int minProtocol)
{
std::vector<pair<unsigned int, CMasternode> > vecMasternodeScores;
std::vector<pair<int, CMasternode> > vecMasternodeRanks;
2015-03-23 13:59:22 +01:00
//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)
2015-03-02 00:09:33 +01:00
{
std::vector<pair<unsigned int, CTxIn> > vecMasternodeScores;
// scan for winner
BOOST_FOREACH(CMasternode& mn, vMasternodes) {
if(mn.protocolVersion < minProtocol) continue;
if(fOnlyActive) {
mn.Check();
if(!mn.IsEnabled()) continue;
2015-03-02 00:09:33 +01:00
}
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
2015-04-03 00:51:08 +02:00
if(Params().NetworkID() == CBaseChainParams::REGTEST) return;
2015-03-02 00:09:33 +01:00
LOCK(cs_vNodes);
if(!darkSendPool.pSubmittedToMasternode) return;
2015-03-22 04:10:34 +01:00
2015-03-02 00:09:33 +01:00
BOOST_FOREACH(CNode* pnode, vNodes)
{
if(darkSendPool.pSubmittedToMasternode->addr == pnode->addr) continue;
if(pnode->fDarkSendMaster){
2015-03-05 09:10:15 +01:00
LogPrintf("Closing Masternode connection %s \n", pnode->addr.ToString().c_str());
2015-03-02 00:09:33 +01:00
pnode->CloseSocketDisconnect();
}
}
}
void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
{
2015-03-05 09:10:15 +01:00
if(fLiteMode) return; //disable all Darksend/Masternode related functionality
if(IsInitialBlockDownload()) return;
2015-03-31 23:21:59 +02:00
LOCK(cs_process_message);
if (strCommand == "mnb") { //Masternode Broadcast
CMasternodeBroadcast mnb;
bool fRequested; //specifically requested?
vRecv >> mnb >> fRequested;
if(mapSeenMasternodeBroadcast.count(mnb.GetHash())) return; //seen
mapSeenMasternodeBroadcast[mnb.GetHash()] = mnb;
int nDoS = 0;
if(!mnb.CheckAndUpdate(nDoS, fRequested)){
if(nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
//failed
return;
}
2015-03-05 09:10:15 +01:00
// 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(mnb.vin, mnb.pubkey)) {
LogPrintf("mnb - Got mismatched pubkey and vin\n");
Misbehaving(pfrom->GetId(), 33);
return;
}
if(fDebug) LogPrintf("mnb - Got NEW Masternode entry %s\n", mnb.addr.ToString().c_str());
// make sure it's still unspent
// - this is checked later by .check() in many places and by ThreadCheckDarkSendPool()
if(mnb.CheckInputsAndAdd(nDoS, fRequested)) {
// use this as a peer
addrman.Add(CAddress(mnb.addr), pfrom->addr, 2*60*60);
} else {
LogPrintf("mnb - Rejected Masternode entry %s\n", mnb.addr.ToString().c_str());
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
}
}
else if (strCommand == "mnp") { //Masternode Ping
CMasternodePing mnp;
vRecv >> mnp;
if(mapSeenMasternodePing.count(mnp.GetHash())) return; //seen
mapSeenMasternodePing[mnp.GetHash()] = mnp;
int nDoS = 0;
if(mnp.CheckAndUpdate(nDoS))
{
//successful, we're done
return;
2015-06-20 22:28:41 +02:00
} else {
//failure
if(nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
}
if(fDebug) LogPrintf("mnp - Couldn't find Masternode entry %s\n", mnp.vin.ToString().c_str());
std::map<COutPoint, int64_t>::iterator i = mWeAskedForMasternodeListEntry.find(mnp.vin.prevout);
if (i != mWeAskedForMasternodeListEntry.end())
{
int64_t t = (*i).second;
if (GetTime() < t) return; // we've asked recently
}
2015-06-23 17:40:08 +02:00
// ask for the mnb info once from the node that sent mnp
LogPrintf("mnp - Asking source node for missing entry %s\n", mnp.vin.ToString().c_str());
pfrom->PushMessage("dseg", mnp.vin);
int64_t askAgain = GetTime() + MASTERNODE_MIN_MNP_SECONDS;
mWeAskedForMasternodeListEntry[mnp.vin.prevout] = askAgain;
2015-03-05 09:10:15 +01:00
} 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
2015-04-03 00:51:08 +02:00
if(!pfrom->addr.IsRFC1918() && Params().NetworkID() == CBaseChainParams::MAIN) {
std::map<CNetAddr, int64_t>::iterator i = mAskedUsForMasternodeList.find(pfrom->addr);
2015-04-03 00:51:08 +02:00
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 i = 0;
BOOST_FOREACH(CMasternode& mn, vMasternodes) {
if(mn.addr.IsRFC1918()) continue; //local network
bool fRequested = true;
2015-04-03 00:51:08 +02:00
if(mn.IsEnabled()) {
2015-03-05 09:10:15 +01:00
if(fDebug) LogPrintf("dseg - Sending Masternode entry - %s \n", mn.addr.ToString().c_str());
if(vin == CTxIn()){
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << CMasternodeBroadcast(mn);
ss << fRequested;
pfrom->PushMessage("mnb", ss);
} else if (vin == mn.vin) {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << CMasternodeBroadcast(mn);
ss << fRequested;
pfrom->PushMessage("mnb", ss);
2015-03-05 09:10:15 +01:00
LogPrintf("dseg - Sent 1 Masternode entries to %s\n", pfrom->addr.ToString().c_str());
return;
}
}
i++;
}
2015-03-05 09:10:15 +01:00
LogPrintf("dseg - Sent %d Masternode entries to %s\n", i, pfrom->addr.ToString().c_str());
}
}
void CMasternodeMan::Remove(CTxIn vin)
{
LOCK(cs);
vector<CMasternode>::iterator it = vMasternodes.begin();
while(it != vMasternodes.end()){
if((*it).vin == vin){
if(fDebug) LogPrintf("CMasternodeMan: Removing Masternode %s - %i now\n", (*it).addr.ToString().c_str(), size() - 1);
vMasternodes.erase(it);
break;
}
++it;
}
}
std::string CMasternodeMan::ToString() const
{
std::ostringstream info;
2015-03-05 09:10:15 +01:00
info << "Masternodes: " << (int)vMasternodes.size() <<
", peers who asked us for Masternode list: " << (int)mAskedUsForMasternodeList.size() <<
", peers we asked for Masternode list: " << (int)mWeAskedForMasternodeList.size() <<
2015-03-06 18:25:48 +01:00
", entries in Masternode list we asked for: " << (int)mWeAskedForMasternodeListEntry.size() <<
", nDsqCount: " << (int)nDsqCount;
return info.str();
}