Files
agrarian/src/masternodeman.cpp
T
2022-02-03 23:45:47 -08:00

1173 lines
42 KiB
C++

// 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 <boost/filesystem.hpp>
#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<int64_t, CTxIn>& t1,
const pair<int64_t, CTxIn>& t2) const
{
return t1.first < t2.first;
}
};
struct CompareScoreTxIn {
bool operator()(const pair<int64_t, CTxIn>& t1,
const pair<int64_t, CTxIn>& t2) const
{
return t1.first < t2.first;
}
};
struct CompareScoreMN {
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");
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<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;
}
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<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 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<CMasternode>::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<uint256, CMasternodeBroadcast>::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<COutPoint, int64_t>::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<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;
}
}
// remove expired mapSeenMasternodeBroadcast
map<uint256, CMasternodeBroadcast>::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<uint256, CMasternodePing>::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<CNetAddr, int64_t>::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<pair<int64_t, CTxIn> > 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<CTxIn>& 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<pair<int64_t, CTxIn> > 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<pair<int, CMasternode> > CMasternodeMan::GetMasternodeRanks(int64_t nBlockHeight, int minProtocol)
{
std::vector<pair<int64_t, CMasternode> > vecMasternodeScores;
std::vector<pair<int, CMasternode> > 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<pair<int64_t, CTxIn> > 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<CNetAddr, int64_t>::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<unsigned char> 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<COutPoint, CPubKey> 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<unsigned char> 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<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
}
// 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<CMasternode>::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();
}