neobytes/src/masternode.cpp
2016-11-13 21:52:34 +04:00

905 lines
36 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, false))) {
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, false, 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 fRequireEnabled, 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;
}
if (sigTime <= GetAdjustedTime() - 60 * 60) {
LogPrintf("CMasternodePing::CheckAndUpdate -- Signature rejected, too far into the past: masternode=%s sigTime=%d GetAdjustedTime()=%d\n", vin.prevout.ToStringShort(), sigTime, GetAdjustedTime());
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;
}
if (fRequireEnabled && !pmn->IsEnabled() && !pmn->IsPreEnabled() && !pmn->IsWatchdogExpired()) 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]);
}
}