mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 21:12:48 +01:00
997 lines
40 KiB
C++
997 lines
40 KiB
C++
// Copyright (c) 2014-2017 The Dash Core developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "activemasternode.h"
|
|
#include "base58.h"
|
|
#include "clientversion.h"
|
|
#include "init.h"
|
|
#include "netbase.h"
|
|
#include "masternode.h"
|
|
#include "masternode-payments.h"
|
|
#include "masternode-sync.h"
|
|
#include "masternodeman.h"
|
|
#include "messagesigner.h"
|
|
#include "script/standard.h"
|
|
#include "util.h"
|
|
#ifdef ENABLE_WALLET
|
|
#include "wallet/wallet.h"
|
|
#endif // ENABLE_WALLET
|
|
|
|
#include "evo/deterministicmns.h"
|
|
|
|
#include <string>
|
|
|
|
|
|
CMasternode::CMasternode() :
|
|
masternode_info_t{ MASTERNODE_ENABLED, PROTOCOL_VERSION, GetAdjustedTime()},
|
|
fAllowMixingTx(true)
|
|
{}
|
|
|
|
CMasternode::CMasternode(CService addr, COutPoint outpoint, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyMasternodeNew, int nProtocolVersionIn) :
|
|
masternode_info_t{ MASTERNODE_ENABLED, nProtocolVersionIn, GetAdjustedTime(),
|
|
outpoint, addr, pubKeyCollateralAddressNew, pubKeyMasternodeNew},
|
|
fAllowMixingTx(true)
|
|
{}
|
|
|
|
CMasternode::CMasternode(const CMasternode& other) :
|
|
masternode_info_t{other},
|
|
lastPing(other.lastPing),
|
|
vchSig(other.vchSig),
|
|
nCollateralMinConfBlockHash(other.nCollateralMinConfBlockHash),
|
|
nBlockLastPaid(other.nBlockLastPaid),
|
|
nPoSeBanScore(other.nPoSeBanScore),
|
|
nPoSeBanHeight(other.nPoSeBanHeight),
|
|
fAllowMixingTx(other.fAllowMixingTx),
|
|
fUnitTest(other.fUnitTest)
|
|
{}
|
|
|
|
CMasternode::CMasternode(const CMasternodeBroadcast& mnb) :
|
|
masternode_info_t{ mnb.nActiveState, mnb.nProtocolVersion, mnb.sigTime,
|
|
mnb.outpoint, mnb.addr, mnb.pubKeyCollateralAddress, mnb.pubKeyMasternode},
|
|
lastPing(mnb.lastPing),
|
|
vchSig(mnb.vchSig),
|
|
fAllowMixingTx(true)
|
|
{}
|
|
|
|
CMasternode::CMasternode(const uint256 &proTxHash, const CDeterministicMNCPtr& dmn) :
|
|
masternode_info_t{ MASTERNODE_ENABLED, DMN_PROTO_VERSION, GetAdjustedTime(),
|
|
dmn->collateralOutpoint, dmn->pdmnState->addr, CKeyID() /* not valid with DIP3 */, dmn->pdmnState->keyIDOwner, dmn->pdmnState->pubKeyOperator, dmn->pdmnState->keyIDVoting},
|
|
fAllowMixingTx(true)
|
|
{
|
|
}
|
|
|
|
//
|
|
// When a new masternode broadcast is sent, update our information
|
|
//
|
|
bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb, CConnman& connman)
|
|
{
|
|
if(mnb.sigTime <= sigTime && !mnb.fRecovery) return false;
|
|
|
|
pubKeyMasternode = mnb.pubKeyMasternode;
|
|
keyIDOwner = mnb.pubKeyMasternode.GetID();
|
|
legacyKeyIDOperator = mnb.pubKeyMasternode.GetID();
|
|
keyIDVoting = mnb.pubKeyMasternode.GetID();
|
|
sigTime = mnb.sigTime;
|
|
vchSig = mnb.vchSig;
|
|
nProtocolVersion = mnb.nProtocolVersion;
|
|
addr = mnb.addr;
|
|
nPoSeBanScore = 0;
|
|
nPoSeBanHeight = 0;
|
|
nTimeLastChecked = 0;
|
|
int nDos = 0;
|
|
if(!mnb.lastPing || (mnb.lastPing && mnb.lastPing.CheckAndUpdate(this, true, nDos, connman))) {
|
|
lastPing = mnb.lastPing;
|
|
mnodeman.mapSeenMasternodePing.insert(std::make_pair(lastPing.GetHash(), lastPing));
|
|
}
|
|
// if it matches our Masternode privkey...
|
|
if(fMasternodeMode && legacyKeyIDOperator == activeMasternodeInfo.legacyKeyIDOperator) {
|
|
nPoSeBanScore = -MASTERNODE_POSE_BAN_MAX_SCORE;
|
|
if(nProtocolVersion == PROTOCOL_VERSION) {
|
|
// ... and PROTOCOL_VERSION, then we've been remotely activated ...
|
|
legacyActiveMasternodeManager.ManageState(connman);
|
|
} else {
|
|
// ... otherwise we need to reactivate our node, do not add it to the list and do not relay
|
|
// but also do not ban the node we get this message from
|
|
LogPrintf("CMasternode::UpdateFromNewBroadcast -- wrong PROTOCOL_VERSION, re-activate your MN: message nProtocolVersion=%d PROTOCOL_VERSION=%d\n", nProtocolVersion, PROTOCOL_VERSION);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Deterministically calculate a given "score" for a Masternode depending on how close it's hash is to
|
|
// the proof of work for that block. The further away they are the better, the furthest will win the election
|
|
// and get paid this block
|
|
//
|
|
arith_uint256 CMasternode::CalculateScore(const uint256& blockHash) const
|
|
{
|
|
// NOTE not called when deterministic masternodes (spork15) are activated
|
|
|
|
// Deterministically calculate a "score" for a Masternode based on any given (block)hash
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << outpoint << nCollateralMinConfBlockHash << blockHash;
|
|
return UintToArith256(ss.GetHash());
|
|
}
|
|
|
|
CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outpoint, const CKeyID& keyID)
|
|
{
|
|
int nHeight;
|
|
return CheckCollateral(outpoint, keyID, nHeight);
|
|
}
|
|
|
|
CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outpoint, const CKeyID& keyID, int& nHeightRet)
|
|
{
|
|
AssertLockHeld(cs_main);
|
|
|
|
Coin coin;
|
|
if(!GetUTXOCoin(outpoint, coin)) {
|
|
return COLLATERAL_UTXO_NOT_FOUND;
|
|
}
|
|
|
|
if(coin.out.nValue != 1000 * COIN) {
|
|
return COLLATERAL_INVALID_AMOUNT;
|
|
}
|
|
|
|
if(keyID.IsNull() || coin.out.scriptPubKey != GetScriptForDestination(keyID)) {
|
|
return COLLATERAL_INVALID_PUBKEY;
|
|
}
|
|
|
|
nHeightRet = coin.nHeight;
|
|
return COLLATERAL_OK;
|
|
}
|
|
|
|
void CMasternode::Check(bool fForce)
|
|
{
|
|
AssertLockHeld(cs_main);
|
|
LOCK(cs);
|
|
|
|
if(ShutdownRequested()) return;
|
|
|
|
if(!fForce && (GetTime() - nTimeLastChecked < MASTERNODE_CHECK_SECONDS)) return;
|
|
nTimeLastChecked = GetTime();
|
|
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state\n", outpoint.ToStringShort(), GetStateString());
|
|
|
|
//once spent, stop doing the checks
|
|
if(IsOutpointSpent()) return;
|
|
|
|
int nHeight = 0;
|
|
if(!fUnitTest) {
|
|
Coin coin;
|
|
if(!GetUTXOCoin(outpoint, coin)) {
|
|
nActiveState = MASTERNODE_OUTPOINT_SPENT;
|
|
LogPrint("masternode", "CMasternode::Check -- Failed to find Masternode UTXO, masternode=%s\n", outpoint.ToStringShort());
|
|
return;
|
|
}
|
|
|
|
nHeight = chainActive.Height();
|
|
}
|
|
|
|
if(IsPoSeBanned()) {
|
|
if(nHeight < nPoSeBanHeight) return; // too early?
|
|
// Otherwise give it a chance to proceed further to do all the usual checks and to change its state.
|
|
// Masternode still will be on the edge and can be banned back easily if it keeps ignoring mnverify
|
|
// or connect attempts. Will require few mnverify messages to strengthen its position in mn list.
|
|
LogPrintf("CMasternode::Check -- Masternode %s is unbanned and back in list now\n", outpoint.ToStringShort());
|
|
DecreasePoSeBanScore();
|
|
} else if(nPoSeBanScore >= MASTERNODE_POSE_BAN_MAX_SCORE) {
|
|
nActiveState = MASTERNODE_POSE_BAN;
|
|
// ban for the whole payment cycle
|
|
nPoSeBanHeight = nHeight + mnodeman.size();
|
|
LogPrintf("CMasternode::Check -- Masternode %s is banned till block %d now\n", outpoint.ToStringShort(), nPoSeBanHeight);
|
|
return;
|
|
}
|
|
|
|
int nActiveStatePrev = nActiveState;
|
|
bool fOurMasternode = fMasternodeMode && activeMasternodeInfo.legacyKeyIDOperator == legacyKeyIDOperator;
|
|
|
|
// masternode doesn't meet payment protocol requirements ...
|
|
bool fRequireUpdate = nProtocolVersion < mnpayments.GetMinMasternodePaymentsProto() ||
|
|
// or it's our own node and we just updated it to the new protocol but we are still waiting for activation ...
|
|
(fOurMasternode && nProtocolVersion < PROTOCOL_VERSION);
|
|
|
|
if(fRequireUpdate) {
|
|
nActiveState = MASTERNODE_UPDATE_REQUIRED;
|
|
if(nActiveStatePrev != nActiveState) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state now\n", outpoint.ToStringShort(), GetStateString());
|
|
}
|
|
return;
|
|
}
|
|
|
|
// keep old masternodes on start, give them a chance to receive updates...
|
|
bool fWaitForPing = !masternodeSync.IsMasternodeListSynced() && !IsPingedWithin(MASTERNODE_MIN_MNP_SECONDS);
|
|
|
|
if(fWaitForPing && !fOurMasternode) {
|
|
// ...but if it was already expired before the initial check - return right away
|
|
if(IsExpired() || IsSentinelPingExpired() || IsNewStartRequired()) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state, waiting for ping\n", outpoint.ToStringShort(), GetStateString());
|
|
return;
|
|
}
|
|
}
|
|
|
|
// don't expire if we are still in "waiting for ping" mode unless it's our own masternode
|
|
if(!fWaitForPing || fOurMasternode) {
|
|
|
|
if(!IsPingedWithin(MASTERNODE_NEW_START_REQUIRED_SECONDS)) {
|
|
nActiveState = MASTERNODE_NEW_START_REQUIRED;
|
|
if(nActiveStatePrev != nActiveState) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state now\n", outpoint.ToStringShort(), GetStateString());
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!IsPingedWithin(MASTERNODE_EXPIRATION_SECONDS)) {
|
|
nActiveState = MASTERNODE_EXPIRED;
|
|
if(nActiveStatePrev != nActiveState) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state now\n", outpoint.ToStringShort(), GetStateString());
|
|
}
|
|
return;
|
|
}
|
|
|
|
// part 1: expire based on dashd ping
|
|
bool fSentinelPingActive = masternodeSync.IsSynced() && mnodeman.IsSentinelPingActive();
|
|
bool fSentinelPingExpired = fSentinelPingActive && !IsPingedWithin(MASTERNODE_SENTINEL_PING_MAX_SECONDS);
|
|
LogPrint("masternode", "CMasternode::Check -- outpoint=%s, GetAdjustedTime()=%d, fSentinelPingExpired=%d\n",
|
|
outpoint.ToStringShort(), GetAdjustedTime(), fSentinelPingExpired);
|
|
|
|
if(fSentinelPingExpired) {
|
|
nActiveState = MASTERNODE_SENTINEL_PING_EXPIRED;
|
|
if(nActiveStatePrev != nActiveState) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state now\n", outpoint.ToStringShort(), GetStateString());
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We require MNs to be in PRE_ENABLED until they either start to expire or receive a ping and go into ENABLED state
|
|
// Works on mainnet/testnet only and not the case on regtest/devnet.
|
|
if (Params().NetworkIDString() != CBaseChainParams::REGTEST && Params().NetworkIDString() != CBaseChainParams::DEVNET) {
|
|
if (lastPing.sigTime - sigTime < MASTERNODE_MIN_MNP_SECONDS) {
|
|
nActiveState = MASTERNODE_PRE_ENABLED;
|
|
if (nActiveStatePrev != nActiveState) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state now\n", outpoint.ToStringShort(), GetStateString());
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(!fWaitForPing || fOurMasternode) {
|
|
// part 2: expire based on sentinel info
|
|
bool fSentinelPingActive = masternodeSync.IsSynced() && mnodeman.IsSentinelPingActive();
|
|
bool fSentinelPingExpired = fSentinelPingActive && !lastPing.fSentinelIsCurrent;
|
|
|
|
LogPrint("masternode", "CMasternode::Check -- outpoint=%s, GetAdjustedTime()=%d, fSentinelPingExpired=%d\n",
|
|
outpoint.ToStringShort(), GetAdjustedTime(), fSentinelPingExpired);
|
|
|
|
if(fSentinelPingExpired) {
|
|
nActiveState = MASTERNODE_SENTINEL_PING_EXPIRED;
|
|
if(nActiveStatePrev != nActiveState) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state now\n", outpoint.ToStringShort(), GetStateString());
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
nActiveState = MASTERNODE_ENABLED; // OK
|
|
if(nActiveStatePrev != nActiveState) {
|
|
LogPrint("masternode", "CMasternode::Check -- Masternode %s is in %s state now\n", outpoint.ToStringShort(), GetStateString());
|
|
}
|
|
}
|
|
|
|
bool CMasternode::IsValidNetAddr()
|
|
{
|
|
return IsValidNetAddr(addr);
|
|
}
|
|
|
|
bool CMasternode::IsValidNetAddr(CService addrIn)
|
|
{
|
|
// TODO: regtest is fine with any addresses for now,
|
|
// should probably be a bit smarter if one day we start to implement tests for this
|
|
return Params().NetworkIDString() == CBaseChainParams::REGTEST ||
|
|
(addrIn.IsIPv4() && IsReachable(addrIn) && addrIn.IsRoutable());
|
|
}
|
|
|
|
masternode_info_t CMasternode::GetInfo() const
|
|
{
|
|
masternode_info_t info{*this};
|
|
info.nTimeLastPing = lastPing.sigTime;
|
|
info.fInfoValid = true;
|
|
return info;
|
|
}
|
|
|
|
std::string CMasternode::StateToString(int nStateIn)
|
|
{
|
|
switch(nStateIn) {
|
|
case MASTERNODE_PRE_ENABLED: return "PRE_ENABLED";
|
|
case MASTERNODE_ENABLED: return "ENABLED";
|
|
case MASTERNODE_EXPIRED: return "EXPIRED";
|
|
case MASTERNODE_OUTPOINT_SPENT: return "OUTPOINT_SPENT";
|
|
case MASTERNODE_UPDATE_REQUIRED: return "UPDATE_REQUIRED";
|
|
case MASTERNODE_SENTINEL_PING_EXPIRED: return "SENTINEL_PING_EXPIRED";
|
|
case MASTERNODE_NEW_START_REQUIRED: return "NEW_START_REQUIRED";
|
|
case MASTERNODE_POSE_BAN: return "POSE_BAN";
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
std::string CMasternode::GetStateString() const
|
|
{
|
|
return StateToString(nActiveState);
|
|
}
|
|
|
|
std::string CMasternode::GetStatus() const
|
|
{
|
|
// TODO: return smth a bit more human readable here
|
|
return GetStateString();
|
|
}
|
|
|
|
void CMasternode::UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScanBack)
|
|
{
|
|
AssertLockHeld(cs_main);
|
|
|
|
if(!pindex) return;
|
|
|
|
if (deterministicMNManager->IsDeterministicMNsSporkActive(pindex->nHeight)) {
|
|
auto dmn = deterministicMNManager->GetListForBlock(pindex->GetBlockHash()).GetMNByCollateral(outpoint);
|
|
if (!dmn || dmn->pdmnState->nLastPaidHeight == -1) {
|
|
LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- not found\n", outpoint.ToStringShort());
|
|
} else {
|
|
nBlockLastPaid = (int)dmn->pdmnState->nLastPaidHeight;
|
|
nTimeLastPaid = chainActive[nBlockLastPaid]->nTime;
|
|
LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- found new %d\n", outpoint.ToStringShort(), nBlockLastPaid);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const CBlockIndex *BlockReading = pindex;
|
|
|
|
CScript mnpayee = GetScriptForDestination(keyIDCollateralAddress);
|
|
// LogPrint("mnpayments", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s\n", outpoint.ToStringShort());
|
|
|
|
LOCK(cs_mapMasternodeBlocks);
|
|
|
|
for (int i = 0; BlockReading && BlockReading->nHeight > nBlockLastPaid && i < nMaxBlocksToScanBack; i++) {
|
|
if(mnpayments.mapMasternodeBlocks.count(BlockReading->nHeight) &&
|
|
mnpayments.mapMasternodeBlocks[BlockReading->nHeight].HasPayeeWithVotes(mnpayee, 2))
|
|
{
|
|
CBlock block;
|
|
if(!ReadBlockFromDisk(block, BlockReading, Params().GetConsensus()))
|
|
continue; // shouldn't really happen
|
|
|
|
CAmount nMasternodePayment = GetMasternodePayment(BlockReading->nHeight, block.vtx[0]->GetValueOut());
|
|
|
|
for (const auto& txout : block.vtx[0]->vout)
|
|
if(mnpayee == txout.scriptPubKey && nMasternodePayment == txout.nValue) {
|
|
nBlockLastPaid = BlockReading->nHeight;
|
|
nTimeLastPaid = BlockReading->nTime;
|
|
LogPrint("mnpayments", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- found new %d\n", outpoint.ToStringShort(), nBlockLastPaid);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (BlockReading->pprev == nullptr) { assert(BlockReading); break; }
|
|
BlockReading = BlockReading->pprev;
|
|
}
|
|
|
|
// Last payment for this masternode wasn't found in latest mnpayments blocks
|
|
// or it was found in mnpayments blocks but wasn't found in the blockchain.
|
|
// LogPrint("mnpayments", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- keeping old %d\n", outpoint.ToStringShort(), nBlockLastPaid);
|
|
}
|
|
|
|
#ifdef ENABLE_WALLET
|
|
bool CMasternodeBroadcast::Create(const std::string& strService, const std::string& strKeyMasternode, const std::string& strTxHash, const std::string& strOutputIndex, std::string& strErrorRet, CMasternodeBroadcast &mnbRet, bool fOffline)
|
|
{
|
|
COutPoint outpoint;
|
|
CPubKey pubKeyCollateralAddressNew;
|
|
CKey keyCollateralAddressNew;
|
|
CPubKey pubKeyMasternodeNew;
|
|
CKey keyMasternodeNew;
|
|
|
|
auto Log = [&strErrorRet](std::string sErr)->bool
|
|
{
|
|
strErrorRet = sErr;
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
};
|
|
|
|
// Wait for sync to finish because mnb simply won't be relayed otherwise
|
|
if (!fOffline && !masternodeSync.IsSynced())
|
|
return Log("Sync in progress. Must wait until sync is complete to start Masternode");
|
|
|
|
if (!CMessageSigner::GetKeysFromSecret(strKeyMasternode, keyMasternodeNew, pubKeyMasternodeNew))
|
|
return Log(strprintf("Invalid masternode key %s", strKeyMasternode));
|
|
|
|
if (!pwalletMain->GetMasternodeOutpointAndKeys(outpoint, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex))
|
|
return Log(strprintf("Could not allocate outpoint %s:%s for masternode %s", strTxHash, strOutputIndex, strService));
|
|
|
|
CService service;
|
|
if (!Lookup(strService.c_str(), service, 0, false))
|
|
return Log(strprintf("Invalid address %s for masternode.", strService));
|
|
int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort();
|
|
if (Params().NetworkIDString() == CBaseChainParams::MAIN) {
|
|
if (service.GetPort() != mainnetDefaultPort)
|
|
return Log(strprintf("Invalid port %u for masternode %s, only %d is supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort));
|
|
} else if (service.GetPort() == mainnetDefaultPort)
|
|
return Log(strprintf("Invalid port %u for masternode %s, %d is the only supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort));
|
|
|
|
return Create(outpoint, service, keyCollateralAddressNew, pubKeyCollateralAddressNew, keyMasternodeNew, pubKeyMasternodeNew, strErrorRet, mnbRet);
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Create(const COutPoint& outpoint, const CService& service, const CKey& keyCollateralAddressNew, const CPubKey& pubKeyCollateralAddressNew, const CKey& keyMasternodeNew, const CPubKey& pubKeyMasternodeNew, std::string &strErrorRet, CMasternodeBroadcast &mnbRet)
|
|
{
|
|
// wait for reindex and/or import to finish
|
|
if (fImporting || fReindex) return false;
|
|
|
|
LogPrint("masternode", "CMasternodeBroadcast::Create -- pubKeyCollateralAddressNew = %s, keyIDOperator = %s\n",
|
|
CBitcoinAddress(pubKeyCollateralAddressNew.GetID()).ToString(),
|
|
pubKeyMasternodeNew.GetID().ToString());
|
|
|
|
auto Log = [&strErrorRet,&mnbRet](std::string sErr)->bool
|
|
{
|
|
strErrorRet = sErr;
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
mnbRet = CMasternodeBroadcast();
|
|
return false;
|
|
};
|
|
|
|
CMasternodePing mnp(outpoint);
|
|
if (!mnp.Sign(keyMasternodeNew, pubKeyMasternodeNew.GetID()))
|
|
return Log(strprintf("Failed to sign ping, masternode=%s", outpoint.ToStringShort()));
|
|
|
|
mnbRet = CMasternodeBroadcast(service, outpoint, pubKeyCollateralAddressNew, pubKeyMasternodeNew, PROTOCOL_VERSION);
|
|
|
|
if (!mnbRet.IsValidNetAddr())
|
|
return Log(strprintf("Invalid IP address, masternode=%s", outpoint.ToStringShort()));
|
|
|
|
mnbRet.lastPing = mnp;
|
|
if (!mnbRet.Sign(keyCollateralAddressNew))
|
|
return Log(strprintf("Failed to sign broadcast, masternode=%s", outpoint.ToStringShort()));
|
|
|
|
return true;
|
|
}
|
|
#endif // ENABLE_WALLET
|
|
|
|
bool CMasternodeBroadcast::SimpleCheck(int& nDos)
|
|
{
|
|
nDos = 0;
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
// make sure addr is valid
|
|
if(!IsValidNetAddr()) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- Invalid addr, rejected: masternode=%s addr=%s\n",
|
|
outpoint.ToStringShort(), addr.ToString());
|
|
return false;
|
|
}
|
|
|
|
// make sure signature isn't in the future (past is OK)
|
|
if (sigTime > GetAdjustedTime() + 60 * 60) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- Signature rejected, too far into the future: masternode=%s\n", outpoint.ToStringShort());
|
|
nDos = 1;
|
|
return false;
|
|
}
|
|
|
|
// empty ping or incorrect sigTime/unknown blockhash
|
|
if(!lastPing || !lastPing.SimpleCheck(nDos)) {
|
|
// one of us is probably forked or smth, just mark it as expired and check the rest of the rules
|
|
nActiveState = MASTERNODE_EXPIRED;
|
|
}
|
|
|
|
if(nProtocolVersion < mnpayments.GetMinMasternodePaymentsProto()) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- outdated Masternode: masternode=%s nProtocolVersion=%d\n", outpoint.ToStringShort(), nProtocolVersion);
|
|
nActiveState = MASTERNODE_UPDATE_REQUIRED;
|
|
}
|
|
|
|
CScript pubkeyScript;
|
|
pubkeyScript = GetScriptForDestination(keyIDCollateralAddress);
|
|
|
|
if(pubkeyScript.size() != 25) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- keyIDCollateralAddress has the wrong size\n");
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
|
|
CScript pubkeyScript2;
|
|
pubkeyScript2 = GetScriptForDestination(legacyKeyIDOperator);
|
|
|
|
if(pubkeyScript2.size() != 25) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- keyIDOperator has the wrong size\n");
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
|
|
int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort();
|
|
if(Params().NetworkIDString() == CBaseChainParams::MAIN) {
|
|
if(addr.GetPort() != mainnetDefaultPort) return false;
|
|
} else if(addr.GetPort() == mainnetDefaultPort) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Update(CMasternode* pmn, int& nDos, CConnman& connman)
|
|
{
|
|
nDos = 0;
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
if(pmn->sigTime == sigTime && !fRecovery) {
|
|
// mapSeenMasternodeBroadcast in CMasternodeMan::CheckMnbAndUpdateMasternodeList should filter legit duplicates
|
|
// but this still can happen if we just started, which is ok, just do nothing here.
|
|
return false;
|
|
}
|
|
|
|
// this broadcast is older than the one that we already have - it's bad and should never happen
|
|
// unless someone is doing something fishy
|
|
if(pmn->sigTime > sigTime) {
|
|
LogPrintf("CMasternodeBroadcast::Update -- Bad sigTime %d (existing broadcast is at %d) for Masternode %s %s\n",
|
|
sigTime, pmn->sigTime, outpoint.ToStringShort(), addr.ToString());
|
|
return false;
|
|
}
|
|
|
|
pmn->Check();
|
|
|
|
// masternode is banned by PoSe
|
|
if(pmn->IsPoSeBanned()) {
|
|
LogPrintf("CMasternodeBroadcast::Update -- Banned by PoSe, masternode=%s\n", outpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
// IsVnAssociatedWithPubkey is validated once in CheckOutpoint, after that they just need to match
|
|
if(pmn->keyIDCollateralAddress != keyIDCollateralAddress) {
|
|
LogPrintf("CMasternodeBroadcast::Update -- Got mismatched keyIDCollateralAddress and outpoint\n");
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
|
|
if (!CheckSignature(nDos)) {
|
|
LogPrintf("CMasternodeBroadcast::Update -- CheckSignature() failed, masternode=%s\n", outpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
// if ther was no masternode broadcast recently or if it matches our Masternode privkey...
|
|
if(!pmn->IsBroadcastedWithin(MASTERNODE_MIN_MNB_SECONDS) || (fMasternodeMode && legacyKeyIDOperator == activeMasternodeInfo.legacyKeyIDOperator)) {
|
|
// take the newest entry
|
|
LogPrintf("CMasternodeBroadcast::Update -- Got UPDATED Masternode entry: addr=%s\n", addr.ToString());
|
|
if(pmn->UpdateFromNewBroadcast(*this, connman)) {
|
|
pmn->Check();
|
|
Relay(connman);
|
|
}
|
|
masternodeSync.BumpAssetLastTime("CMasternodeBroadcast::Update");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::CheckOutpoint(int& nDos)
|
|
{
|
|
// we are a masternode with the same outpoint (i.e. already activated) and this mnb is ours (matches our Masternode privkey)
|
|
// so nothing to do here for us
|
|
if(fMasternodeMode && outpoint == activeMasternodeInfo.outpoint && legacyKeyIDOperator == activeMasternodeInfo.legacyKeyIDOperator) {
|
|
return false;
|
|
}
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
int nHeight;
|
|
CollateralStatus err = CheckCollateral(outpoint, keyIDCollateralAddress, nHeight);
|
|
if (err == COLLATERAL_UTXO_NOT_FOUND) {
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Failed to find Masternode UTXO, masternode=%s\n", outpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
if (err == COLLATERAL_INVALID_AMOUNT) {
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should have 1000 DASH, masternode=%s\n", outpoint.ToStringShort());
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
|
|
if(err == COLLATERAL_INVALID_PUBKEY) {
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should match keyIDCollateralAddress, masternode=%s\n", outpoint.ToStringShort());
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
|
|
if(chainActive.Height() - nHeight + 1 < Params().GetConsensus().nMasternodeMinimumConfirmations) {
|
|
LogPrintf("CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO must have at least %d confirmations, masternode=%s\n",
|
|
Params().GetConsensus().nMasternodeMinimumConfirmations, outpoint.ToStringShort());
|
|
// UTXO is legit but has not enough confirmations.
|
|
// Maybe we miss few blocks, let this mnb be checked again later.
|
|
mnodeman.mapSeenMasternodeBroadcast.erase(GetHash());
|
|
return false;
|
|
}
|
|
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO verified\n");
|
|
|
|
// Verify that sig time is legit, should be at least not earlier than the timestamp of the block
|
|
// at which collateral became nMasternodeMinimumConfirmations blocks deep.
|
|
// NOTE: this is not accurate because block timestamp is NOT guaranteed to be 100% correct one.
|
|
CBlockIndex* pRequiredConfIndex = chainActive[nHeight + Params().GetConsensus().nMasternodeMinimumConfirmations - 1]; // block where tx got nMasternodeMinimumConfirmations
|
|
if(pRequiredConfIndex->GetBlockTime() > sigTime) {
|
|
LogPrintf("CMasternodeBroadcast::CheckOutpoint -- Bad sigTime %d (%d conf block is at %d) for Masternode %s %s\n",
|
|
sigTime, Params().GetConsensus().nMasternodeMinimumConfirmations, pRequiredConfIndex->GetBlockTime(), outpoint.ToStringShort(), addr.ToString());
|
|
return false;
|
|
}
|
|
|
|
if (!CheckSignature(nDos)) {
|
|
LogPrintf("CMasternodeBroadcast::CheckOutpoint -- CheckSignature() failed, masternode=%s\n", outpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
// remember the block hash when collateral for this masternode had minimum required confirmations
|
|
nCollateralMinConfBlockHash = pRequiredConfIndex->GetBlockHash();
|
|
|
|
return true;
|
|
}
|
|
|
|
uint256 CMasternodeBroadcast::GetHash() const
|
|
{
|
|
// Note: doesn't match serialization
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << outpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing format
|
|
ss << pubKeyCollateralAddress;
|
|
ss << sigTime;
|
|
return ss.GetHash();
|
|
}
|
|
|
|
uint256 CMasternodeBroadcast::GetSignatureHash() const
|
|
{
|
|
// TODO: replace with "return SerializeHash(*this);" after migration to 70209
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << outpoint;
|
|
ss << addr;
|
|
ss << pubKeyCollateralAddress;
|
|
ss << pubKeyMasternode;
|
|
ss << sigTime;
|
|
ss << nProtocolVersion;
|
|
return ss.GetHash();
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Sign(const CKey& keyCollateralAddress)
|
|
{
|
|
std::string strError;
|
|
|
|
sigTime = GetAdjustedTime();
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::SignHash(hash, keyCollateralAddress, vchSig)) {
|
|
LogPrintf("CMasternodeBroadcast::Sign -- SignHash() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!CHashSigner::VerifyHash(hash, keyIDCollateralAddress, vchSig, strError)) {
|
|
LogPrintf("CMasternodeBroadcast::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
} else {
|
|
std::string strMessage = addr.ToString(false) + std::to_string(sigTime) +
|
|
keyIDCollateralAddress.ToString() + legacyKeyIDOperator.ToString() +
|
|
std::to_string(nProtocolVersion);
|
|
|
|
if (!CMessageSigner::SignMessage(strMessage, vchSig, keyCollateralAddress)) {
|
|
LogPrintf("CMasternodeBroadcast::Sign -- SignMessage() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if(!CMessageSigner::VerifyMessage(keyIDCollateralAddress, vchSig, strMessage, strError)) {
|
|
LogPrintf("CMasternodeBroadcast::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::CheckSignature(int& nDos) const
|
|
{
|
|
std::string strError = "";
|
|
nDos = 0;
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::VerifyHash(hash, keyIDCollateralAddress, vchSig, strError)) {
|
|
// maybe it's in old format
|
|
std::string strMessage = addr.ToString(false) + std::to_string(sigTime) +
|
|
keyIDCollateralAddress.ToString() + legacyKeyIDOperator.ToString() +
|
|
std::to_string(nProtocolVersion);
|
|
|
|
if (!CMessageSigner::VerifyMessage(keyIDCollateralAddress, vchSig, strMessage, strError)){
|
|
// nope, not in old format either
|
|
LogPrintf("CMasternodeBroadcast::CheckSignature -- Got bad Masternode announce signature, error: %s\n", strError);
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
std::string strMessage = addr.ToString(false) + std::to_string(sigTime) +
|
|
keyIDCollateralAddress.ToString() + legacyKeyIDOperator.ToString() +
|
|
std::to_string(nProtocolVersion);
|
|
|
|
if(!CMessageSigner::VerifyMessage(keyIDCollateralAddress, vchSig, strMessage, strError)){
|
|
LogPrintf("CMasternodeBroadcast::CheckSignature -- Got bad Masternode announce signature, error: %s\n", strError);
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMasternodeBroadcast::Relay(CConnman& connman) const
|
|
{
|
|
// Do not relay until fully synced
|
|
if(!masternodeSync.IsSynced()) {
|
|
LogPrint("masternode", "CMasternodeBroadcast::Relay -- won't relay until fully synced\n");
|
|
return;
|
|
}
|
|
|
|
CInv inv(MSG_MASTERNODE_ANNOUNCE, GetHash());
|
|
connman.RelayInv(inv);
|
|
}
|
|
|
|
uint256 CMasternodePing::GetHash() const
|
|
{
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
// TODO: replace with "return SerializeHash(*this);" after migration to 70209
|
|
ss << masternodeOutpoint;
|
|
ss << blockHash;
|
|
ss << sigTime;
|
|
ss << fSentinelIsCurrent;
|
|
ss << nSentinelVersion;
|
|
ss << nDaemonVersion;
|
|
} else {
|
|
// Note: doesn't match serialization
|
|
|
|
ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing format
|
|
ss << sigTime;
|
|
}
|
|
return ss.GetHash();
|
|
}
|
|
|
|
uint256 CMasternodePing::GetSignatureHash() const
|
|
{
|
|
return GetHash();
|
|
}
|
|
|
|
CMasternodePing::CMasternodePing(const COutPoint& outpoint)
|
|
{
|
|
LOCK(cs_main);
|
|
if (!chainActive.Tip() || chainActive.Height() < 12) return;
|
|
|
|
masternodeOutpoint = outpoint;
|
|
blockHash = chainActive[chainActive.Height() - 12]->GetBlockHash();
|
|
sigTime = GetAdjustedTime();
|
|
nDaemonVersion = CLIENT_VERSION;
|
|
}
|
|
|
|
bool CMasternodePing::Sign(const CKey& keyMasternode, const CKeyID& keyIDOperator)
|
|
{
|
|
std::string strError;
|
|
|
|
sigTime = GetAdjustedTime();
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::SignHash(hash, keyMasternode, vchSig)) {
|
|
LogPrintf("CMasternodePing::Sign -- SignHash() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!CHashSigner::VerifyHash(hash, keyIDOperator, vchSig, strError)) {
|
|
LogPrintf("CMasternodePing::Sign -- VerifyHash() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
} else {
|
|
std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() +
|
|
std::to_string(sigTime);
|
|
|
|
if (!CMessageSigner::SignMessage(strMessage, vchSig, keyMasternode)) {
|
|
LogPrintf("CMasternodePing::Sign -- SignMessage() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if(!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) {
|
|
LogPrintf("CMasternodePing::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePing::CheckSignature(CKeyID& keyIDOperator, int &nDos) const
|
|
{
|
|
std::string strError = "";
|
|
nDos = 0;
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::VerifyHash(hash, keyIDOperator, vchSig, strError)) {
|
|
std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() +
|
|
std::to_string(sigTime);
|
|
|
|
if(!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) {
|
|
LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", masternodeOutpoint.ToStringShort(), strError);
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() +
|
|
std::to_string(sigTime);
|
|
|
|
if (!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) {
|
|
LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", masternodeOutpoint.ToStringShort(), strError);
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePing::SimpleCheck(int& nDos)
|
|
{
|
|
// don't ban by default
|
|
nDos = 0;
|
|
|
|
if (sigTime > GetAdjustedTime() + 60 * 60) {
|
|
LogPrintf("CMasternodePing::SimpleCheck -- Signature rejected, too far into the future, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
nDos = 1;
|
|
return false;
|
|
}
|
|
|
|
{
|
|
AssertLockHeld(cs_main);
|
|
BlockMap::iterator mi = mapBlockIndex.find(blockHash);
|
|
if (mi == mapBlockIndex.end()) {
|
|
LogPrint("masternode", "CMasternodePing::SimpleCheck -- Masternode ping is invalid, unknown block hash: masternode=%s blockHash=%s\n", masternodeOutpoint.ToStringShort(), blockHash.ToString());
|
|
// maybe we stuck or forked so we shouldn't ban this node, just fail to accept this ping
|
|
// TODO: or should we also request this block?
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogPrint("masternode", "CMasternodePing::SimpleCheck -- Masternode ping verified: masternode=%s blockHash=%s sigTime=%d\n", masternodeOutpoint.ToStringShort(), blockHash.ToString(), sigTime);
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePing::CheckAndUpdate(CMasternode* pmn, bool fFromNewBroadcast, int& nDos, CConnman& connman)
|
|
{
|
|
AssertLockHeld(cs_main);
|
|
|
|
// don't ban by default
|
|
nDos = 0;
|
|
|
|
if (!SimpleCheck(nDos)) {
|
|
return false;
|
|
}
|
|
|
|
if (pmn == nullptr) {
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Couldn't find Masternode entry, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
if(!fFromNewBroadcast) {
|
|
if (pmn->IsUpdateRequired()) {
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- masternode protocol is outdated, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
if (pmn->IsNewStartRequired()) {
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- masternode is completely expired, new start is required, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
{
|
|
BlockMap::iterator mi = mapBlockIndex.find(blockHash);
|
|
if ((*mi).second && (*mi).second->nHeight < chainActive.Height() - 24) {
|
|
LogPrintf("CMasternodePing::CheckAndUpdate -- Masternode ping is invalid, block hash is too old: masternode=%s blockHash=%s\n", masternodeOutpoint.ToStringShort(), blockHash.ToString());
|
|
// nDos = 1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- New ping: masternode=%s blockHash=%s sigTime=%d\n", masternodeOutpoint.ToStringShort(), blockHash.ToString(), sigTime);
|
|
|
|
// LogPrintf("mnping - Found corresponding mn for outpoint: %s\n", masternodeOutpoint.ToStringShort());
|
|
// update only if there is no known ping for this masternode or
|
|
// last ping was more then MASTERNODE_MIN_MNP_SECONDS-60 ago comparing to this one
|
|
if (pmn->IsPingedWithin(MASTERNODE_MIN_MNP_SECONDS - 60, sigTime)) {
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Masternode ping arrived too early, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
//nDos = 1; //disable, this is happening frequently and causing banned peers
|
|
return false;
|
|
}
|
|
|
|
if (!CheckSignature(pmn->legacyKeyIDOperator, nDos)) return false;
|
|
|
|
// so, ping seems to be ok
|
|
|
|
// if we are still syncing and there was no known ping for this mn for quite a while
|
|
// (NOTE: assuming that MASTERNODE_EXPIRATION_SECONDS/2 should be enough to finish mn list sync)
|
|
if(!masternodeSync.IsMasternodeListSynced() && !pmn->IsPingedWithin(MASTERNODE_EXPIRATION_SECONDS/2)) {
|
|
// let's bump sync timeout
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- bumping sync timeout, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
masternodeSync.BumpAssetLastTime("CMasternodePing::CheckAndUpdate");
|
|
}
|
|
|
|
// let's store this ping as the last one
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Masternode ping accepted, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
pmn->lastPing = *this;
|
|
|
|
// and update mnodeman.mapSeenMasternodeBroadcast.lastPing which is probably outdated
|
|
CMasternodeBroadcast mnb(*pmn);
|
|
uint256 hash = mnb.GetHash();
|
|
if (mnodeman.mapSeenMasternodeBroadcast.count(hash)) {
|
|
mnodeman.mapSeenMasternodeBroadcast[hash].second.lastPing = *this;
|
|
}
|
|
|
|
// force update, ignoring cache
|
|
pmn->Check(true);
|
|
// relay ping for nodes in ENABLED/EXPIRED/SENTINEL_PING_EXPIRED state only, skip everyone else
|
|
if (!pmn->IsEnabled() && !pmn->IsExpired() && !pmn->IsSentinelPingExpired()) return false;
|
|
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Masternode ping acceepted and relayed, masternode=%s\n", masternodeOutpoint.ToStringShort());
|
|
Relay(connman);
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMasternodePing::Relay(CConnman& connman)
|
|
{
|
|
// Do not relay until fully synced
|
|
if(!masternodeSync.IsSynced()) {
|
|
LogPrint("masternode", "CMasternodePing::Relay -- won't relay until fully synced\n");
|
|
return;
|
|
}
|
|
|
|
CInv inv(MSG_MASTERNODE_PING, GetHash());
|
|
connman.RelayInv(inv);
|
|
}
|
|
|
|
std::string CMasternodePing::GetSentinelString() const
|
|
{
|
|
return nSentinelVersion > DEFAULT_SENTINEL_VERSION ? SafeIntVersionToString(nSentinelVersion) : "Unknown";
|
|
}
|
|
|
|
std::string CMasternodePing::GetDaemonString() const
|
|
{
|
|
return nDaemonVersion > DEFAULT_DAEMON_VERSION ? FormatVersion(nDaemonVersion) : "Unknown";
|
|
}
|
|
|
|
void CMasternode::AddGovernanceVote(uint256 nGovernanceObjectHash)
|
|
{
|
|
// Insert a zero value, or not. Then increment the value regardless. This
|
|
// ensures the value is in the map.
|
|
const auto& pair = mapGovernanceObjectsVotedOn.emplace(nGovernanceObjectHash, 0);
|
|
pair.first->second++;
|
|
}
|
|
|
|
void CMasternode::RemoveGovernanceObject(uint256 nGovernanceObjectHash)
|
|
{
|
|
// Whether or not the govobj hash exists in the map first is irrelevant.
|
|
mapGovernanceObjectsVotedOn.erase(nGovernanceObjectHash);
|
|
}
|
|
|
|
/**
|
|
* FLAG GOVERNANCE ITEMS AS DIRTY
|
|
*
|
|
* - When masternode come and go on the network, we must flag the items they voted on to recalc it's cached flags
|
|
*
|
|
*/
|
|
void CMasternode::FlagGovernanceItemsAsDirty()
|
|
{
|
|
for (auto& govObjHashPair : mapGovernanceObjectsVotedOn) {
|
|
mnodeman.AddDirtyGovernanceObjectHash(govObjHashPair.first);
|
|
}
|
|
}
|