17cebbed15
* Since we send all mnb's now regardless of mn state, ping check for sigTime being too old is obsolete (and wrong). Also removing fRequireEnabled, this logic is deprecated too it seems. * remove (pre-)enabled check in CMasternodeMan::Add
897 lines
35 KiB
C++
897 lines
35 KiB
C++
// Copyright (c) 2014-2016 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 "consensus/validation.h"
|
|
#include "darksend.h"
|
|
#include "init.h"
|
|
#include "governance.h"
|
|
#include "masternode.h"
|
|
#include "masternode-payments.h"
|
|
#include "masternode-sync.h"
|
|
#include "masternodeman.h"
|
|
#include "util.h"
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
|
|
CMasternode::CMasternode() :
|
|
vin(),
|
|
addr(),
|
|
pubKeyCollateralAddress(),
|
|
pubKeyMasternode(),
|
|
lastPing(),
|
|
vchSig(),
|
|
sigTime(GetAdjustedTime()),
|
|
nLastDsq(0),
|
|
nTimeLastChecked(0),
|
|
nTimeLastPaid(0),
|
|
nTimeLastWatchdogVote(0),
|
|
nActiveState(MASTERNODE_ENABLED),
|
|
nCacheCollateralBlock(0),
|
|
nBlockLastPaid(0),
|
|
nProtocolVersion(PROTOCOL_VERSION),
|
|
nPoSeBanScore(0),
|
|
nPoSeBanHeight(0),
|
|
fAllowMixingTx(true),
|
|
fUnitTest(false)
|
|
{}
|
|
|
|
CMasternode::CMasternode(CService addrNew, CTxIn vinNew, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyMasternodeNew, int nProtocolVersionIn) :
|
|
vin(vinNew),
|
|
addr(addrNew),
|
|
pubKeyCollateralAddress(pubKeyCollateralAddressNew),
|
|
pubKeyMasternode(pubKeyMasternodeNew),
|
|
lastPing(),
|
|
vchSig(),
|
|
sigTime(GetAdjustedTime()),
|
|
nLastDsq(0),
|
|
nTimeLastChecked(0),
|
|
nTimeLastPaid(0),
|
|
nTimeLastWatchdogVote(0),
|
|
nActiveState(MASTERNODE_ENABLED),
|
|
nCacheCollateralBlock(0),
|
|
nBlockLastPaid(0),
|
|
nProtocolVersion(nProtocolVersionIn),
|
|
nPoSeBanScore(0),
|
|
nPoSeBanHeight(0),
|
|
fAllowMixingTx(true),
|
|
fUnitTest(false)
|
|
{}
|
|
|
|
CMasternode::CMasternode(const CMasternode& other) :
|
|
vin(other.vin),
|
|
addr(other.addr),
|
|
pubKeyCollateralAddress(other.pubKeyCollateralAddress),
|
|
pubKeyMasternode(other.pubKeyMasternode),
|
|
lastPing(other.lastPing),
|
|
vchSig(other.vchSig),
|
|
sigTime(other.sigTime),
|
|
nLastDsq(other.nLastDsq),
|
|
nTimeLastChecked(other.nTimeLastChecked),
|
|
nTimeLastPaid(other.nTimeLastPaid),
|
|
nTimeLastWatchdogVote(other.nTimeLastWatchdogVote),
|
|
nActiveState(other.nActiveState),
|
|
nCacheCollateralBlock(other.nCacheCollateralBlock),
|
|
nBlockLastPaid(other.nBlockLastPaid),
|
|
nProtocolVersion(other.nProtocolVersion),
|
|
nPoSeBanScore(other.nPoSeBanScore),
|
|
nPoSeBanHeight(other.nPoSeBanHeight),
|
|
fAllowMixingTx(other.fAllowMixingTx),
|
|
fUnitTest(other.fUnitTest)
|
|
{}
|
|
|
|
CMasternode::CMasternode(const CMasternodeBroadcast& mnb) :
|
|
vin(mnb.vin),
|
|
addr(mnb.addr),
|
|
pubKeyCollateralAddress(mnb.pubKeyCollateralAddress),
|
|
pubKeyMasternode(mnb.pubKeyMasternode),
|
|
lastPing(mnb.lastPing),
|
|
vchSig(mnb.vchSig),
|
|
sigTime(mnb.sigTime),
|
|
nLastDsq(mnb.nLastDsq),
|
|
nTimeLastChecked(0),
|
|
nTimeLastPaid(0),
|
|
nTimeLastWatchdogVote(mnb.sigTime),
|
|
nActiveState(MASTERNODE_ENABLED),
|
|
nCacheCollateralBlock(0),
|
|
nBlockLastPaid(0),
|
|
nProtocolVersion(mnb.nProtocolVersion),
|
|
nPoSeBanScore(0),
|
|
nPoSeBanHeight(0),
|
|
fAllowMixingTx(true),
|
|
fUnitTest(false)
|
|
{}
|
|
|
|
//
|
|
// When a new masternode broadcast is sent, update our information
|
|
//
|
|
bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb)
|
|
{
|
|
if(mnb.sigTime <= sigTime) return false;
|
|
|
|
pubKeyMasternode = mnb.pubKeyMasternode;
|
|
sigTime = mnb.sigTime;
|
|
vchSig = mnb.vchSig;
|
|
nProtocolVersion = mnb.nProtocolVersion;
|
|
addr = mnb.addr;
|
|
nPoSeBanScore = 0;
|
|
nPoSeBanHeight = 0;
|
|
nTimeLastChecked = 0;
|
|
nTimeLastWatchdogVote = mnb.sigTime;
|
|
int nDos = 0;
|
|
if(mnb.lastPing == CMasternodePing() || (mnb.lastPing != CMasternodePing() && mnb.lastPing.CheckAndUpdate(nDos))) {
|
|
lastPing = mnb.lastPing;
|
|
mnodeman.mapSeenMasternodePing.insert(std::make_pair(lastPing.GetHash(), lastPing));
|
|
}
|
|
// if it matches our Masternode privkey...
|
|
if(fMasterNode && pubKeyMasternode == activeMasternode.pubKeyMasternode) {
|
|
nPoSeBanScore = -MASTERNODE_POSE_BAN_MAX_SCORE;
|
|
if(nProtocolVersion == PROTOCOL_VERSION) {
|
|
// ... and PROTOCOL_VERSION, then we've been remotely activated ...
|
|
activeMasternode.ManageState();
|
|
} 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)
|
|
{
|
|
uint256 aux = ArithToUint256(UintToArith256(vin.prevout.hash) + vin.prevout.n);
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << blockHash;
|
|
arith_uint256 hash2 = UintToArith256(ss.GetHash());
|
|
|
|
CHashWriter ss2(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss2 << blockHash;
|
|
ss2 << aux;
|
|
arith_uint256 hash3 = UintToArith256(ss2.GetHash());
|
|
|
|
return (hash3 > hash2 ? hash3 - hash2 : hash2 - hash3);
|
|
}
|
|
|
|
void CMasternode::Check(bool fForce)
|
|
{
|
|
LOCK(cs);
|
|
|
|
static int64_t nTimeStart = GetTime();
|
|
|
|
LogPrint("masternode", "CMasternode::Check start -- vin %s\n", vin.prevout.ToStringShort());
|
|
|
|
//once spent, stop doing the checks
|
|
if(nActiveState == MASTERNODE_OUTPOINT_SPENT) return;
|
|
|
|
if(ShutdownRequested()) return;
|
|
|
|
if(!fForce && (GetTime() - nTimeLastChecked < MASTERNODE_CHECK_SECONDS)) return;
|
|
nTimeLastChecked = GetTime();
|
|
|
|
int nHeight = 0;
|
|
if(!fUnitTest) {
|
|
TRY_LOCK(cs_main, lockMain);
|
|
if(!lockMain) return;
|
|
|
|
CCoins coins;
|
|
if(!pcoinsTip->GetCoins(vin.prevout.hash, coins) ||
|
|
(unsigned int)vin.prevout.n>=coins.vout.size() ||
|
|
coins.vout[vin.prevout.n].IsNull()) {
|
|
nActiveState = MASTERNODE_OUTPOINT_SPENT;
|
|
LogPrint("masternode", "CMasternode::Check -- Failed to find Masternode UTXO, masternode=%s\n", vin.prevout.ToStringShort());
|
|
return;
|
|
}
|
|
|
|
nHeight = chainActive.Height();
|
|
}
|
|
|
|
// keep old masternodes on start, give them a chance to receive an updated ping without removal/expiry
|
|
if(!masternodeSync.IsMasternodeListSynced()) nTimeStart = GetTime();
|
|
bool fWaitForPing = (GetTime() - nTimeStart < MASTERNODE_MIN_MNP_SECONDS);
|
|
|
|
if(nActiveState == MASTERNODE_POSE_BAN) {
|
|
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", vin.prevout.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", vin.prevout.ToStringShort(), nPoSeBanHeight);
|
|
return;
|
|
}
|
|
|
|
// masternode doesn't meet payment protocol requirements ...
|
|
bool fRemove = 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 ...
|
|
(pubKeyMasternode == activeMasternode.pubKeyMasternode && nProtocolVersion < PROTOCOL_VERSION);
|
|
|
|
if(fRemove) {
|
|
// it should be removed from the list
|
|
nActiveState = MASTERNODE_REMOVE;
|
|
|
|
// RESCAN AFFECTED VOTES
|
|
FlagGovernanceItemsAsDirty();
|
|
return;
|
|
}
|
|
|
|
bool fWatchdogActive = mnodeman.IsWatchdogActive();
|
|
bool fWatchdogExpired = (fWatchdogActive && ((GetTime() - nTimeLastWatchdogVote) > MASTERNODE_WATCHDOG_MAX_SECONDS));
|
|
|
|
LogPrint("masternode", "CMasternode::Check -- vin %s, nTimeLastWatchdogVote %d, GetTime() %d, fWatchdogExpired %d\n",
|
|
vin.prevout.ToStringShort(), nTimeLastWatchdogVote, GetTime(), fWatchdogExpired);
|
|
|
|
if(fWatchdogExpired) {
|
|
nActiveState = MASTERNODE_WATCHDOG_EXPIRED;
|
|
return;
|
|
}
|
|
|
|
if(!fWaitForPing && !IsPingedWithin(MASTERNODE_EXPIRATION_SECONDS)) {
|
|
nActiveState = MASTERNODE_EXPIRED;
|
|
// RESCAN AFFECTED VOTES
|
|
FlagGovernanceItemsAsDirty();
|
|
return;
|
|
}
|
|
|
|
if(lastPing.sigTime - sigTime < MASTERNODE_MIN_MNP_SECONDS) {
|
|
nActiveState = MASTERNODE_PRE_ENABLED;
|
|
return;
|
|
}
|
|
|
|
nActiveState = MASTERNODE_ENABLED; // OK
|
|
}
|
|
|
|
bool CMasternode::IsValidNetAddr()
|
|
{
|
|
// 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 ||
|
|
(addr.IsIPv4() && IsReachable(addr) && addr.IsRoutable());
|
|
}
|
|
|
|
masternode_info_t CMasternode::GetInfo()
|
|
{
|
|
masternode_info_t info;
|
|
info.vin = vin;
|
|
info.addr = addr;
|
|
info.pubKeyCollateralAddress = pubKeyCollateralAddress;
|
|
info.pubKeyMasternode = pubKeyMasternode;
|
|
info.sigTime = sigTime;
|
|
info.nLastDsq = nLastDsq;
|
|
info.nTimeLastChecked = nTimeLastChecked;
|
|
info.nTimeLastPaid = nTimeLastPaid;
|
|
info.nTimeLastWatchdogVote = nTimeLastWatchdogVote;
|
|
info.nActiveState = nActiveState;
|
|
info.nProtocolVersion = nProtocolVersion;
|
|
info.fInfoValid = true;
|
|
return info;
|
|
}
|
|
|
|
std::string CMasternode::StateToString(int nStateIn)
|
|
{
|
|
switch(nStateIn) {
|
|
case CMasternode::MASTERNODE_PRE_ENABLED: return "PRE_ENABLED";
|
|
case CMasternode::MASTERNODE_ENABLED: return "ENABLED";
|
|
case CMasternode::MASTERNODE_EXPIRED: return "EXPIRED";
|
|
case CMasternode::MASTERNODE_OUTPOINT_SPENT: return "OUTPOINT_SPENT";
|
|
case CMasternode::MASTERNODE_REMOVE: return "REMOVE";
|
|
case CMasternode::MASTERNODE_WATCHDOG_EXPIRED: return "WATCHDOG_EXPIRED";
|
|
case CMasternode::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();
|
|
}
|
|
|
|
int CMasternode::GetCollateralAge()
|
|
{
|
|
int nHeight;
|
|
{
|
|
TRY_LOCK(cs_main, lockMain);
|
|
if(!lockMain || !chainActive.Tip()) return -1;
|
|
nHeight = chainActive.Height();
|
|
}
|
|
|
|
if (nCacheCollateralBlock == 0) {
|
|
int nInputAge = GetInputAge(vin);
|
|
if(nInputAge > 0) {
|
|
nCacheCollateralBlock = nHeight - nInputAge;
|
|
} else {
|
|
return nInputAge;
|
|
}
|
|
}
|
|
|
|
return nHeight - nCacheCollateralBlock;
|
|
}
|
|
|
|
void CMasternode::UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScanBack)
|
|
{
|
|
if(!pindex) return;
|
|
|
|
const CBlockIndex *BlockReading = pindex;
|
|
|
|
CScript mnpayee = GetScriptForDestination(pubKeyCollateralAddress.GetID());
|
|
// LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s\n", vin.prevout.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())) // shouldn't really happen
|
|
continue;
|
|
|
|
CAmount nMasternodePayment = GetMasternodePayment(BlockReading->nHeight, block.vtx[0].GetValueOut());
|
|
|
|
BOOST_FOREACH(CTxOut txout, block.vtx[0].vout)
|
|
if(mnpayee == txout.scriptPubKey && nMasternodePayment == txout.nValue) {
|
|
nBlockLastPaid = BlockReading->nHeight;
|
|
nTimeLastPaid = BlockReading->nTime;
|
|
LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- found new %d\n", vin.prevout.ToStringShort(), nBlockLastPaid);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (BlockReading->pprev == NULL) { 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("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- keeping old %d\n", vin.prevout.ToStringShort(), nBlockLastPaid);
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Create(std::string strService, std::string strKeyMasternode, std::string strTxHash, std::string strOutputIndex, std::string& strErrorRet, CMasternodeBroadcast &mnbRet, bool fOffline)
|
|
{
|
|
CTxIn txin;
|
|
CPubKey pubKeyCollateralAddressNew;
|
|
CKey keyCollateralAddressNew;
|
|
CPubKey pubKeyMasternodeNew;
|
|
CKey keyMasternodeNew;
|
|
|
|
//need correct blocks to send ping
|
|
if(!fOffline && !masternodeSync.IsBlockchainSynced()) {
|
|
strErrorRet = "Sync in progress. Must wait until sync is complete to start Masternode";
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
if(!darkSendSigner.GetKeysFromSecret(strKeyMasternode, keyMasternodeNew, pubKeyMasternodeNew)) {
|
|
strErrorRet = strprintf("Invalid masternode key %s", strKeyMasternode);
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
if(!pwalletMain->GetMasternodeVinAndKeys(txin, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex)) {
|
|
strErrorRet = strprintf("Could not allocate txin %s:%s for masternode %s", strTxHash, strOutputIndex, strService);
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
CService service = CService(strService);
|
|
int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort();
|
|
if(Params().NetworkIDString() == CBaseChainParams::MAIN) {
|
|
if(service.GetPort() != mainnetDefaultPort) {
|
|
strErrorRet = strprintf("Invalid port %u for masternode %s, only %d is supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort);
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
} else if (service.GetPort() == mainnetDefaultPort) {
|
|
strErrorRet = strprintf("Invalid port %u for masternode %s, %d is the only supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort);
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
return Create(txin, CService(strService), keyCollateralAddressNew, pubKeyCollateralAddressNew, keyMasternodeNew, pubKeyMasternodeNew, strErrorRet, mnbRet);
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollateralAddressNew, CPubKey pubKeyCollateralAddressNew, CKey keyMasternodeNew, 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, pubKeyMasternodeNew.GetID() = %s\n",
|
|
CBitcoinAddress(pubKeyCollateralAddressNew.GetID()).ToString(),
|
|
pubKeyMasternodeNew.GetID().ToString());
|
|
|
|
|
|
CMasternodePing mnp(txin);
|
|
if(!mnp.Sign(keyMasternodeNew, pubKeyMasternodeNew)) {
|
|
strErrorRet = strprintf("Failed to sign ping, masternode=%s", txin.prevout.ToStringShort());
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
mnbRet = CMasternodeBroadcast();
|
|
return false;
|
|
}
|
|
|
|
mnbRet = CMasternodeBroadcast(service, txin, pubKeyCollateralAddressNew, pubKeyMasternodeNew, PROTOCOL_VERSION);
|
|
|
|
if(!mnbRet.IsValidNetAddr()) {
|
|
strErrorRet = strprintf("Invalid IP address, masternode=%s", txin.prevout.ToStringShort());
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
mnbRet = CMasternodeBroadcast();
|
|
return false;
|
|
}
|
|
|
|
mnbRet.lastPing = mnp;
|
|
if(!mnbRet.Sign(keyCollateralAddressNew)) {
|
|
strErrorRet = strprintf("Failed to sign broadcast, masternode=%s", txin.prevout.ToStringShort());
|
|
LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
mnbRet = CMasternodeBroadcast();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::SimpleCheck(int& nDos)
|
|
{
|
|
nDos = 0;
|
|
|
|
// make sure addr is valid
|
|
if(!IsValidNetAddr()) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- Invalid addr, rejected: masternode=%s addr=%s\n",
|
|
vin.prevout.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", vin.prevout.ToStringShort());
|
|
nDos = 1;
|
|
return false;
|
|
}
|
|
|
|
// empty ping or incorrect sigTime/blockhash
|
|
if(lastPing == CMasternodePing() || !lastPing.CheckAndUpdate(nDos, true)) {
|
|
return false;
|
|
}
|
|
|
|
if(nProtocolVersion < mnpayments.GetMinMasternodePaymentsProto()) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- ignoring outdated Masternode: masternode=%s nProtocolVersion=%d\n", vin.prevout.ToStringShort(), nProtocolVersion);
|
|
return false;
|
|
}
|
|
|
|
CScript pubkeyScript;
|
|
pubkeyScript = GetScriptForDestination(pubKeyCollateralAddress.GetID());
|
|
|
|
if(pubkeyScript.size() != 25) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- pubKeyCollateralAddress has the wrong size\n");
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
|
|
CScript pubkeyScript2;
|
|
pubkeyScript2 = GetScriptForDestination(pubKeyMasternode.GetID());
|
|
|
|
if(pubkeyScript2.size() != 25) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- pubKeyMasternode has the wrong size\n");
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
|
|
if(!vin.scriptSig.empty()) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- Ignore Not Empty ScriptSig %s\n",vin.ToString());
|
|
return false;
|
|
}
|
|
|
|
if (!CheckSignature(nDos)) {
|
|
LogPrintf("CMasternodeBroadcast::SimpleCheck -- CheckSignature() failed, masternode=%s\n", vin.prevout.ToStringShort());
|
|
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)
|
|
{
|
|
if(pmn->sigTime == sigTime) {
|
|
// 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 true;
|
|
}
|
|
|
|
// 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, vin.prevout.ToStringShort(), addr.ToString());
|
|
return false;
|
|
}
|
|
|
|
pmn->Check();
|
|
|
|
// masternode is banned by PoSe
|
|
if(pmn->IsPoSeBanned()) {
|
|
LogPrintf("CMasternodeBroadcast::Update -- Banned by PoSe, masternode=%s\n", vin.prevout.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
// IsVnAssociatedWithPubkey is validated once in CheckOutpoint, after that they just need to match
|
|
if(pmn->pubKeyCollateralAddress != pubKeyCollateralAddress) {
|
|
LogPrintf("CMasternodeMan::Update -- Got mismatched pubKeyCollateralAddress and vin\n");
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
|
|
// if ther was no masternode broadcast recently or if it matches our Masternode privkey...
|
|
if(!pmn->IsBroadcastedWithin(MASTERNODE_MIN_MNB_SECONDS) || (fMasterNode && pubKeyMasternode == activeMasternode.pubKeyMasternode)) {
|
|
// take the newest entry
|
|
LogPrintf("CMasternodeBroadcast::Update -- Got UPDATED Masternode entry: addr=%s\n", addr.ToString());
|
|
if(pmn->UpdateFromNewBroadcast((*this))) {
|
|
pmn->Check();
|
|
Relay();
|
|
}
|
|
masternodeSync.AddedMasternodeList();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::CheckOutpoint(int& nDos)
|
|
{
|
|
// we are a masternode with the same vin (i.e. already activated) and this mnb is ours (matches our Masternode privkey)
|
|
// so nothing to do here for us
|
|
if(fMasterNode && vin.prevout == activeMasternode.vin.prevout && pubKeyMasternode == activeMasternode.pubKeyMasternode) {
|
|
return false;
|
|
}
|
|
|
|
{
|
|
TRY_LOCK(cs_main, lockMain);
|
|
if(!lockMain) {
|
|
// not mnb fault, let it to be checked again later
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Failed to aquire lock, addr=%s", addr.ToString());
|
|
mnodeman.mapSeenMasternodeBroadcast.erase(GetHash());
|
|
return false;
|
|
}
|
|
|
|
CCoins coins;
|
|
if(!pcoinsTip->GetCoins(vin.prevout.hash, coins) ||
|
|
(unsigned int)vin.prevout.n>=coins.vout.size() ||
|
|
coins.vout[vin.prevout.n].IsNull()) {
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Failed to find Masternode UTXO, masternode=%s\n", vin.prevout.ToStringShort());
|
|
return false;
|
|
}
|
|
if(coins.vout[vin.prevout.n].nValue != 1000 * COIN) {
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should have 1000 DASH, masternode=%s\n", vin.prevout.ToStringShort());
|
|
return false;
|
|
}
|
|
if(chainActive.Height() - coins.nHeight + 1 < Params().GetConsensus().nMasternodeMinimumConfirmations) {
|
|
LogPrintf("CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO must have at least %d confirmations, masternode=%s\n",
|
|
Params().GetConsensus().nMasternodeMinimumConfirmations, vin.prevout.ToStringShort());
|
|
// maybe we miss few blocks, let this mnb to be checked again later
|
|
mnodeman.mapSeenMasternodeBroadcast.erase(GetHash());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO verified\n");
|
|
|
|
// make sure the vout that was signed is related to the transaction that spawned the Masternode
|
|
// - this is expensive, so it's only done once per Masternode
|
|
if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubKeyCollateralAddress)) {
|
|
LogPrintf("CMasternodeMan::CheckOutpoint -- Got mismatched pubKeyCollateralAddress and vin\n");
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
|
|
// verify that sig time is legit in past
|
|
// should be at least not earlier than block when 1000 DASH tx got nMasternodeMinimumConfirmations
|
|
uint256 hashBlock = uint256();
|
|
CTransaction tx2;
|
|
GetTransaction(vin.prevout.hash, tx2, Params().GetConsensus(), hashBlock, true);
|
|
{
|
|
LOCK(cs_main);
|
|
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
|
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
|
CBlockIndex* pMNIndex = (*mi).second; // block for 1000 DASH tx -> 1 confirmation
|
|
CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + Params().GetConsensus().nMasternodeMinimumConfirmations - 1]; // block where tx got nMasternodeMinimumConfirmations
|
|
if(pConfIndex->GetBlockTime() > sigTime) {
|
|
LogPrintf("CMasternodeBroadcast::CheckOutpoint -- Bad sigTime %d (%d conf block is at %d) for Masternode %s %s\n",
|
|
sigTime, Params().GetConsensus().nMasternodeMinimumConfirmations, pConfIndex->GetBlockTime(), vin.prevout.ToStringShort(), addr.ToString());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Sign(CKey& keyCollateralAddress)
|
|
{
|
|
std::string strError;
|
|
std::string strMessage;
|
|
|
|
sigTime = GetAdjustedTime();
|
|
|
|
strMessage = addr.ToString(false) + boost::lexical_cast<std::string>(sigTime) +
|
|
pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() +
|
|
boost::lexical_cast<std::string>(nProtocolVersion);
|
|
|
|
if(!darkSendSigner.SignMessage(strMessage, vchSig, keyCollateralAddress)) {
|
|
LogPrintf("CMasternodeBroadcast::Sign -- SignMessage() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)) {
|
|
LogPrintf("CMasternodeBroadcast::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::CheckSignature(int& nDos)
|
|
{
|
|
std::string strMessage;
|
|
std::string strError = "";
|
|
nDos = 0;
|
|
|
|
//
|
|
// REMOVE AFTER MIGRATION TO 12.1
|
|
//
|
|
if(nProtocolVersion < 70201) {
|
|
std::string vchPubkeyCollateralAddress(pubKeyCollateralAddress.begin(), pubKeyCollateralAddress.end());
|
|
std::string vchPubkeyMasternode(pubKeyMasternode.begin(), pubKeyMasternode.end());
|
|
strMessage = addr.ToString(false) + boost::lexical_cast<std::string>(sigTime) +
|
|
vchPubkeyCollateralAddress + vchPubkeyMasternode + boost::lexical_cast<std::string>(nProtocolVersion);
|
|
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckSignature -- sanitized strMessage: %s pubKeyCollateralAddress address: %s sig: %s\n",
|
|
SanitizeString(strMessage), CBitcoinAddress(pubKeyCollateralAddress.GetID()).ToString(),
|
|
EncodeBase64(&vchSig[0], vchSig.size()));
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)) {
|
|
if(addr.ToString() != addr.ToString(false)) {
|
|
// maybe it's wrong format, try again with the old one
|
|
strMessage = addr.ToString() + boost::lexical_cast<std::string>(sigTime) +
|
|
vchPubkeyCollateralAddress + vchPubkeyMasternode + boost::lexical_cast<std::string>(nProtocolVersion);
|
|
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckSignature -- second try, sanitized strMessage: %s pubKeyCollateralAddress address: %s sig: %s\n",
|
|
SanitizeString(strMessage), CBitcoinAddress(pubKeyCollateralAddress.GetID()).ToString(),
|
|
EncodeBase64(&vchSig[0], vchSig.size()));
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)) {
|
|
// didn't work either
|
|
LogPrintf("CMasternodeBroadcast::CheckSignature -- Got bad Masternode announce signature, second try, sanitized error: %s\n",
|
|
SanitizeString(strError));
|
|
// don't ban for old masternodes, their sigs could be broken because of the bug
|
|
return false;
|
|
}
|
|
} else {
|
|
// nope, sig is actually wrong
|
|
LogPrintf("CMasternodeBroadcast::CheckSignature -- Got bad Masternode announce signature, sanitized error: %s\n",
|
|
SanitizeString(strError));
|
|
// don't ban for old masternodes, their sigs could be broken because of the bug
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// END REMOVE
|
|
//
|
|
strMessage = addr.ToString(false) + boost::lexical_cast<std::string>(sigTime) +
|
|
pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() +
|
|
boost::lexical_cast<std::string>(nProtocolVersion);
|
|
|
|
LogPrint("masternode", "CMasternodeBroadcast::CheckSignature -- strMessage: %s pubKeyCollateralAddress address: %s sig: %s\n", strMessage, CBitcoinAddress(pubKeyCollateralAddress.GetID()).ToString(), EncodeBase64(&vchSig[0], vchSig.size()));
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)){
|
|
LogPrintf("CMasternodeBroadcast::CheckSignature -- Got bad Masternode announce signature, error: %s\n", strError);
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMasternodeBroadcast::Relay()
|
|
{
|
|
CInv inv(MSG_MASTERNODE_ANNOUNCE, GetHash());
|
|
RelayInv(inv);
|
|
}
|
|
|
|
CMasternodePing::CMasternodePing(CTxIn& vinNew)
|
|
{
|
|
LOCK(cs_main);
|
|
if (!chainActive.Tip() || chainActive.Height() < 12) return;
|
|
|
|
vin = vinNew;
|
|
blockHash = chainActive[chainActive.Height() - 12]->GetBlockHash();
|
|
sigTime = GetAdjustedTime();
|
|
vchSig = std::vector<unsigned char>();
|
|
}
|
|
|
|
bool CMasternodePing::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
|
|
{
|
|
std::string strError;
|
|
std::string strMasterNodeSignMessage;
|
|
|
|
sigTime = GetAdjustedTime();
|
|
std::string strMessage = vin.ToString() + blockHash.ToString() + boost::lexical_cast<std::string>(sigTime);
|
|
|
|
if(!darkSendSigner.SignMessage(strMessage, vchSig, keyMasternode)) {
|
|
LogPrintf("CMasternodePing::Sign -- SignMessage() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
|
LogPrintf("CMasternodePing::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePing::CheckSignature(CPubKey& pubKeyMasternode, int &nDos)
|
|
{
|
|
std::string strMessage = vin.ToString() + blockHash.ToString() + boost::lexical_cast<std::string>(sigTime);
|
|
std::string strError = "";
|
|
nDos = 0;
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
|
LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", vin.prevout.ToStringShort(), strError);
|
|
nDos = 33;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePing::CheckAndUpdate(int& nDos, bool fSimpleCheck)
|
|
{
|
|
if (sigTime > GetAdjustedTime() + 60 * 60) {
|
|
LogPrintf("CMasternodePing::CheckAndUpdate -- Signature rejected, too far into the future, masternode=%s\n", vin.prevout.ToStringShort());
|
|
nDos = 1;
|
|
return false;
|
|
}
|
|
|
|
{
|
|
LOCK(cs_main);
|
|
BlockMap::iterator mi = mapBlockIndex.find(blockHash);
|
|
if (mi == mapBlockIndex.end()) {
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Masternode ping is invalid, unknown block hash: masternode=%s blockHash=%s\n", vin.prevout.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;
|
|
}
|
|
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", vin.prevout.ToStringShort(), blockHash.ToString());
|
|
// Do nothing here (no Masternode update, no mnping relay)
|
|
// Let this node to be visible but fail to accept mnping
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (fSimpleCheck) {
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- ping verified in fSimpleCheck mode: masternode=%s blockHash=%s sigTime=%d\n", vin.prevout.ToStringShort(), blockHash.ToString(), sigTime);
|
|
return true;
|
|
}
|
|
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- New ping: masternode=%s blockHash=%s sigTime=%d\n", vin.prevout.ToStringShort(), blockHash.ToString(), sigTime);
|
|
|
|
// see if we have this Masternode
|
|
CMasternode* pmn = mnodeman.Find(vin);
|
|
|
|
if (pmn == NULL || pmn->nProtocolVersion < mnpayments.GetMinMasternodePaymentsProto()) {
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Couldn't find compatible Masternode entry, masternode=%s\n", vin.prevout.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
// LogPrintf("mnping - Found corresponding mn for vin: %s\n", vin.prevout.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", vin.prevout.ToStringShort());
|
|
//nDos = 1; //disable, this is happening frequently and causing banned peers
|
|
return false;
|
|
}
|
|
|
|
if (!CheckSignature(pmn->pubKeyMasternode, nDos)) return false;
|
|
|
|
// so, ping seems to be ok, let's store it
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Masternode ping accepted, masternode=%s\n", vin.prevout.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].lastPing = *this;
|
|
}
|
|
|
|
pmn->Check(true); // force update, ignoring cache
|
|
if (!pmn->IsEnabled()) return false;
|
|
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Masternode ping acceepted and relayed, masternode=%s\n", vin.prevout.ToStringShort());
|
|
Relay();
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMasternodePing::Relay()
|
|
{
|
|
CInv inv(MSG_MASTERNODE_PING, GetHash());
|
|
RelayInv(inv);
|
|
}
|
|
|
|
void CMasternode::AddGovernanceVote(uint256 nGovernanceObjectHash)
|
|
{
|
|
if(mapGovernanceObjectsVotedOn.count(nGovernanceObjectHash)) {
|
|
mapGovernanceObjectsVotedOn[nGovernanceObjectHash]++;
|
|
} else {
|
|
mapGovernanceObjectsVotedOn.insert(std::make_pair(nGovernanceObjectHash, 1));
|
|
}
|
|
}
|
|
|
|
void CMasternode::RemoveGovernanceObject(uint256 nGovernanceObjectHash)
|
|
{
|
|
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.find(nGovernanceObjectHash);
|
|
if(it == mapGovernanceObjectsVotedOn.end()) {
|
|
return;
|
|
}
|
|
mapGovernanceObjectsVotedOn.erase(it);
|
|
}
|
|
|
|
void CMasternode::UpdateWatchdogVoteTime()
|
|
{
|
|
LOCK(cs);
|
|
nTimeLastWatchdogVote = GetTime();
|
|
}
|
|
|
|
/**
|
|
* 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()
|
|
{
|
|
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();
|
|
while(it != mapGovernanceObjectsVotedOn.end()){
|
|
CGovernanceObject *pObj = governance.FindGovernanceObject((*it).first);
|
|
|
|
if(pObj) pObj->InvalidateVoteCache();
|
|
++it;
|
|
}
|
|
|
|
std::vector<uint256> vecDirty;
|
|
{
|
|
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();
|
|
while(it != mapGovernanceObjectsVotedOn.end()) {
|
|
vecDirty.push_back(it->first);
|
|
++it;
|
|
}
|
|
}
|
|
for(size_t i = 0; i < vecDirty.size(); ++i) {
|
|
mnodeman.AddDirtyGovernanceObjectHash(vecDirty[i]);
|
|
}
|
|
}
|