From 342bda5fdcd6ace0e33b5979ef1116fbedf8bb56 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 21 Oct 2016 01:11:30 +0400 Subject: [PATCH] new PoSe - MNVERIFY (#1066) --- src/darksend.cpp | 2 + src/dsnotificationinterface.cpp | 2 + src/init.cpp | 1 + src/main.cpp | 14 + src/masternode.cpp | 285 ++++++++--------- src/masternode.h | 90 +++++- src/masternodeman.cpp | 532 ++++++++++++++++++++++++++++++-- src/masternodeman.h | 54 ++-- src/protocol.cpp | 7 +- src/protocol.h | 4 +- 10 files changed, 785 insertions(+), 206 deletions(-) diff --git a/src/darksend.cpp b/src/darksend.cpp index 0b327e3a5..d6dee0518 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -1578,6 +1578,7 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) LogPrintf("CDarksendPool::DoAutomaticDenominating -- error connecting\n"); strAutoDenomResult = _("Error connecting to Masternode."); dsq.nTime = 0; //remove node + pmn->nPoSeBanScore++; continue; } } @@ -1624,6 +1625,7 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) } else { LogPrintf("CDarksendPool::DoAutomaticDenominating -- can't connect %s\n", pmn->vin.ToString()); nTries++; + pmn->nPoSeBanScore++; continue; } } diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index b2346a7d1..426e473a0 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -5,6 +5,7 @@ #include "dsnotificationinterface.h" #include "darksend.h" #include "governance.h" +#include "masternodeman.h" #include "masternode-payments.h" #include "masternode-sync.h" @@ -18,6 +19,7 @@ CDSNotificationInterface::~CDSNotificationInterface() void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex) { + mnodeman.UpdatedBlockTip(pindex); darkSendPool.UpdatedBlockTip(pindex); mnpayments.UpdatedBlockTip(pindex); governance.UpdatedBlockTip(pindex); diff --git a/src/init.cpp b/src/init.cpp index 1150a5207..018fcb801 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1880,6 +1880,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // force UpdatedBlockTip to initialize pCurrentBlockIndex for DS, MN payments and budgets // but don't call it directly to prevent triggering of other listeners like zmq etc. // GetMainSignals().UpdatedBlockTip(chainActive.Tip()); + mnodeman.UpdatedBlockTip(chainActive.Tip()); darkSendPool.UpdatedBlockTip(chainActive.Tip()); mnpayments.UpdatedBlockTip(chainActive.Tip()); masternodeSync.UpdatedBlockTip(chainActive.Tip()); diff --git a/src/main.cpp b/src/main.cpp index f03cd829e..dc24da685 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4959,6 +4959,10 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) case MSG_GOVERNANCE_OBJECT_VOTE: return governance.HaveVoteForHash(inv.hash); + + case MSG_MASTERNODE_VERIFY: + return mnodeman.mapSeenMasternodeVerification.count(inv.hash); + } // Don't know what it is, just say we already got one @@ -5205,6 +5209,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } + if (!pushed && inv.type == MSG_MASTERNODE_VERIFY) { + if(mnodeman.mapSeenMasternodeVerification.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mnodeman.mapSeenMasternodeVerification[inv.hash]; + pfrom->PushMessage(NetMsgType::MNVERIFY, ss); + pushed = true; + } + } + if (!pushed) vNotFound.push_back(inv); } diff --git a/src/masternode.cpp b/src/masternode.cpp index 16c9c644b..740f36d4f 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -32,6 +32,7 @@ CMasternode::CMasternode() : nCacheCollateralBlock(0), nBlockLastPaid(0), nProtocolVersion(PROTOCOL_VERSION), + nPoSeBanScore(0), fAllowMixingTx(true), fUnitTest(false) {} @@ -52,6 +53,7 @@ CMasternode::CMasternode(CService addrNew, CTxIn vinNew, CPubKey pubKeyCollatera nCacheCollateralBlock(0), nBlockLastPaid(0), nProtocolVersion(nProtocolVersionIn), + nPoSeBanScore(0), fAllowMixingTx(true), fUnitTest(false) {} @@ -72,6 +74,7 @@ CMasternode::CMasternode(const CMasternode& other) : nCacheCollateralBlock(other.nCacheCollateralBlock), nBlockLastPaid(other.nBlockLastPaid), nProtocolVersion(other.nProtocolVersion), + nPoSeBanScore(other.nPoSeBanScore), fAllowMixingTx(other.fAllowMixingTx), fUnitTest(other.fUnitTest) {} @@ -92,6 +95,7 @@ CMasternode::CMasternode(const CMasternodeBroadcast& mnb) : nCacheCollateralBlock(0), nBlockLastPaid(0), nProtocolVersion(mnb.nProtocolVersion), + nPoSeBanScore(0), fAllowMixingTx(true), fUnitTest(false) {} @@ -101,21 +105,34 @@ CMasternode::CMasternode(const CMasternodeBroadcast& mnb) : // bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb) { - if(mnb.sigTime > sigTime) { - pubKeyMasternode = mnb.pubKeyMasternode; - sigTime = mnb.sigTime; - vchSig = mnb.vchSig; - nProtocolVersion = mnb.nProtocolVersion; - addr = mnb.addr; - nTimeLastChecked = 0; - 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)); - } - return true; + if(mnb.sigTime <= sigTime) return false; + + pubKeyMasternode = mnb.pubKeyMasternode; + sigTime = mnb.sigTime; + vchSig = mnb.vchSig; + nProtocolVersion = mnb.nProtocolVersion; + addr = mnb.addr; + nPoSeBanScore = 0; + nTimeLastChecked = 0; + 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)); } - return false; + // 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; } // @@ -143,10 +160,9 @@ void CMasternode::Check(bool fForce) { LOCK(cs); - bool fWatchdogActive = mnodeman.IsWatchdogActive(); + static int64_t nTimeStart = GetTime(); - LogPrint("masternode", "CMasternode::Check start -- vin = %s\n", - vin.prevout.ToStringShort()); + LogPrint("masternode", "CMasternode::Check start -- vin %s\n", vin.prevout.ToStringShort()); //once spent, stop doing the checks if(nActiveState == MASTERNODE_OUTPOINT_SPENT) return; @@ -156,9 +172,18 @@ void CMasternode::Check(bool fForce) if(!fForce && (GetTime() - nTimeLastChecked < MASTERNODE_CHECK_SECONDS)) return; nTimeLastChecked = GetTime(); - if((nActiveState == MASTERNODE_WATCHDOG_EXPIRED) && !fWatchdogActive) { - // Redo the checks - nActiveState = MASTERNODE_ENABLED; + 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; + } } // masternode doesn't meet payment protocol requirements ... @@ -166,6 +191,24 @@ void CMasternode::Check(bool fForce) // 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); + // 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(IsPingedWithin(MASTERNODE_POSE_BAN_SECONDS)) { + // Still alive? Good luck with that. + return; + } else { + // It's finally dead, good... + // or did we just start our node and it's too early to decide? + fRemove = !fWaitForPing; + } + } else if(nPoSeBanScore >= MASTERNODE_POSE_BAN_MAX_SCORE) { + nActiveState = MASTERNODE_POSE_BAN; + return; + } + if(fRemove) { // it should be removed from the list nActiveState = MASTERNODE_REMOVE; @@ -175,47 +218,26 @@ void CMasternode::Check(bool fForce) return; } - if(!IsPingedWithin(MASTERNODE_EXPIRATION_SECONDS)) { - if(nActiveState != MASTERNODE_WATCHDOG_EXPIRED) { - nActiveState = MASTERNODE_EXPIRED; - } + 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) { - if(nActiveState != MASTERNODE_WATCHDOG_EXPIRED) { - nActiveState = MASTERNODE_PRE_ENABLED; - } - - return; - } - - if(!fUnitTest) { - CValidationState state; - CMutableTransaction tx = CMutableTransaction(); - CTxOut txout = CTxOut(999.99*COIN, mnodeman.dummyScriptPubkey); - tx.vin.push_back(vin); - tx.vout.push_back(txout); - - { - TRY_LOCK(cs_main, lockMain); - if(!lockMain) return; - - if(!AcceptToMemoryPool(mempool, state, CTransaction(tx), false, NULL, false, true, true)) { - nActiveState = MASTERNODE_OUTPOINT_SPENT; - return; - } - } - } - - 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; + nActiveState = MASTERNODE_PRE_ENABLED; return; } @@ -257,6 +279,7 @@ std::string CMasternode::GetStatus() 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"; } } @@ -398,12 +421,20 @@ bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollater return true; } -bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) +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::CheckAndUpdate -- Signature rejected, too far into the future: masternode=%s\n", vin.prevout.ToStringShort()); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- Signature rejected, too far into the future: masternode=%s\n", vin.prevout.ToStringShort()); nDos = 1; return false; } @@ -414,7 +445,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) } if(nProtocolVersion < mnpayments.GetMinMasternodePaymentsProto()) { - LogPrintf("CMasternodeBroadcast::CheckAndUpdate -- ignoring outdated Masternode: masternode=%s nProtocolVersion=%d\n", vin.prevout.ToStringShort(), nProtocolVersion); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- ignoring outdated Masternode: masternode=%s nProtocolVersion=%d\n", vin.prevout.ToStringShort(), nProtocolVersion); return false; } @@ -422,7 +453,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) pubkeyScript = GetScriptForDestination(pubKeyCollateralAddress.GetID()); if(pubkeyScript.size() != 25) { - LogPrintf("CMasternodeBroadcast::CheckAndUpdate -- pubKeyCollateralAddress has the wrong size\n"); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- pubKeyCollateralAddress has the wrong size\n"); nDos = 100; return false; } @@ -431,18 +462,18 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) pubkeyScript2 = GetScriptForDestination(pubKeyMasternode.GetID()); if(pubkeyScript2.size() != 25) { - LogPrintf("CMasternodeBroadcast::CheckAndUpdate -- pubKeyMasternode has the wrong size\n"); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- pubKeyMasternode has the wrong size\n"); nDos = 100; return false; } if(!vin.scriptSig.empty()) { - LogPrintf("CMasternodeBroadcast::CheckAndUpdate -- Ignore Not Empty ScriptSig %s\n",vin.ToString()); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- Ignore Not Empty ScriptSig %s\n",vin.ToString()); return false; } if (!CheckSignature(nDos)) { - LogPrintf("CMasternodeBroadcast::CheckAndUpdate -- CheckSignature() failed, masternode=%s\n", vin.prevout.ToStringShort()); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- CheckSignature() failed, masternode=%s\n", vin.prevout.ToStringShort()); return false; } @@ -451,28 +482,47 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) if(addr.GetPort() != mainnetDefaultPort) return false; } else if(addr.GetPort() == mainnetDefaultPort) return false; - //search existing Masternode list, this is where we update existing Masternodes with new mnb broadcasts - CMasternode* pmn = mnodeman.Find(vin); + return true; +} - // no such masternode, nothing to update - if(pmn == NULL) 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 or equal than the one that we already have - it's bad and should never happen + // this broadcast is older than the one that we already have - it's bad and should never happen // unless someone is doing something fishy - // (mapSeenMasternodeBroadcast in CMasternodeMan::ProcessMessage should filter legit duplicates) - if(pmn->sigTime >= sigTime) { - LogPrintf("CMasternodeBroadcast::CheckAndUpdate -- Bad sigTime %d (existing broadcast is at %d) for Masternode %s %s\n", + 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; } - // masternode is not enabled yet/already, nothing to update - if(!pmn->IsEnabled()) return true; + pmn->Check(); - // IsVnAssociatedWithPubkey is validated once below, after that they just need to match - if(pmn->pubKeyCollateralAddress == pubKeyCollateralAddress && !pmn->IsBroadcastedWithin(MASTERNODE_MIN_MNB_SECONDS)) { - //take the newest entry - LogPrintf("CMasternodeBroadcast::CheckAndUpdate -- Got UPDATED Masternode entry: addr=%s\n", addr.ToString()); + // masternode is banned by PoSe + if(pmn->IsPoSeBanned()) { + LogPrintf("CMasternodeBroadcast::Update -- Banned by PoSe, masternode=%s\n", vin.prevout.ToStringShort()); + return false; + } + + // masternode is not enabled yet/already, nothing to update + if(!pmn->IsEnabled()) 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(); // normally masternode should be in pre-enabled status after update, if not - do not relay @@ -486,66 +536,49 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) return true; } -bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDos) +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 true; - } - - // incorrect ping or its sigTime - if(lastPing == CMasternodePing() || !lastPing.CheckAndUpdate(nDos, false, true)) { return false; } - // search existing Masternode list - CMasternode* pmn = mnodeman.Find(vin); - - if(pmn != NULL) { - // nothing to do here if we already know about this masternode and it's (pre)enabled - if(pmn->IsEnabled() || pmn->IsPreEnabled() || pmn->IsWatchdogExpired()) return true; - // if it's not (pre)enabled, remove old MN first and continue - mnodeman.Remove(pmn->vin); - } - - if(GetInputAge(vin) < Params().GetConsensus().nMasternodeMinimumConfirmations) { - LogPrintf("CMasternodeBroadcast::CheckInputsAndAdd -- Input must have at least %d confirmations\n", Params().GetConsensus().nMasternodeMinimumConfirmations); - // maybe we miss few blocks, let this mnb to be checked again later - mnodeman.mapSeenMasternodeBroadcast.erase(GetHash()); - return false; - } - - CValidationState state; - CMutableTransaction dummyTx = CMutableTransaction(); - CTxOut dummyTxOut = CTxOut(999.99*COIN, mnodeman.dummyScriptPubkey); - dummyTx.vin.push_back(vin); - dummyTx.vout.push_back(dummyTxOut); - { TRY_LOCK(cs_main, lockMain); if(!lockMain) { // not mnb fault, let it to be checked again later - LogPrint("masternode", "CMasternodeBroadcast::CheckInputsAndAdd -- Failed to aquire lock, addr=%s", addr.ToString()); + LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Failed to aquire lock, addr=%s", addr.ToString()); mnodeman.mapSeenMasternodeBroadcast.erase(GetHash()); return false; } - if(!AcceptToMemoryPool(mempool, state, CTransaction(dummyTx), false, NULL, false, true, true)) { - //set nDos - LogPrint("masternode", "CMasternodeBroadcast::CheckInputsAndAdd -- Failed to accepted Masternode entry to mempool: dummyTx=%s", dummyTx.ToString()); - state.IsInvalid(nDos); + 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=\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::CheckInputsAndAdd -- Accepted Masternode entry to mempool (dry-run mode)\n"); - + 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::CheckInputsAndAdd -- Got mismatched pubKeyCollateralAddress and vin\n"); + LogPrintf("CMasternodeMan::CheckOutpoint -- Got mismatched pubKeyCollateralAddress and vin\n"); nDos = 33; return false; } @@ -562,39 +595,13 @@ bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDos) 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::CheckInputsAndAdd -- Bad sigTime %d (%d conf block is at %d) for Masternode %s %s\n", + 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; } } } - // if it matches our Masternode privkey... - if(fMasterNode && pubKeyMasternode == activeMasternode.pubKeyMasternode) { - if(nProtocolVersion == PROTOCOL_VERSION) { - // ... and PROTOCOL_VERSION, then we've been remotely activated ... - activeMasternode.EnableRemoteMasterNode(vin, addr); - } else { - // ... otherwise we need to reactivate our node, don not add it to the list and do not relay - // but also do not ban the node we get this message from - LogPrintf("CMasternodeBroadcast::CheckInputsAndAdd -- wrong PROTOCOL_VERSION, re-activate your MN: message nProtocolVersion=%d PROTOCOL_VERSION=%d\n", nProtocolVersion, PROTOCOL_VERSION); - return false; - } - } - - LogPrintf("CMasternodeBroadcast::CheckInputsAndAdd -- Got NEW Masternode entry: masternode=%s sigTime=%lld addr=%s\n", vin.prevout.ToStringShort(), sigTime, addr.ToString()); - CMasternode mn(*this); - mnodeman.Add(mn); - - bool isLocal = addr.IsRFC1918() || addr.IsLocal(); - if(Params().NetworkIDString() == CBaseChainParams::REGTEST) { - isLocal = false; - } - - if(!isLocal) { - Relay(); - } - return true; } diff --git a/src/masternode.h b/src/masternode.h index 64e508c84..71b790080 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -14,14 +14,15 @@ class CMasternode; class CMasternodeBroadcast; class CMasternodePing; -static const int MASTERNODE_MIN_MNP_SECONDS = 10 * 60; -static const int MASTERNODE_MIN_MNB_SECONDS = 5 * 60; -static const int MASTERNODE_MIN_DSEG_SECONDS = 10 * 60; -static const int MASTERNODE_EXPIRATION_SECONDS = 65 * 60; -static const int MASTERNODE_REMOVAL_SECONDS = 75 * 60; -static const int MASTERNODE_CHECK_SECONDS = 5; -static const int MASTERNODE_WATCHDOG_MAX_SECONDS = 2 * 60 * 60; +static const int MASTERNODE_MIN_MNP_SECONDS = 10 * 60; +static const int MASTERNODE_MIN_MNB_SECONDS = 5 * 60; +static const int MASTERNODE_EXPIRATION_SECONDS = 65 * 60; +static const int MASTERNODE_REMOVAL_SECONDS = 75 * 60; +static const int MASTERNODE_CHECK_SECONDS = 5; +static const int MASTERNODE_WATCHDOG_MAX_SECONDS = 2 * 60 * 60; +static const int MASTERNODE_POSE_BAN_SECONDS = 24 * 60 * 60; +static const int MASTERNODE_POSE_BAN_MAX_SCORE = 5; // // The Masternode Ping Class : Contains a different serialize method for sending pings from masternodes throughout the network // @@ -144,7 +145,8 @@ public: MASTERNODE_EXPIRED, MASTERNODE_OUTPOINT_SPENT, MASTERNODE_REMOVE, - MASTERNODE_WATCHDOG_EXPIRED + MASTERNODE_WATCHDOG_EXPIRED, + MASTERNODE_POSE_BAN }; CTxIn vin; @@ -162,6 +164,7 @@ public: int nCacheCollateralBlock; int nBlockLastPaid; int nProtocolVersion; + int nPoSeBanScore; bool fAllowMixingTx; bool fUnitTest; @@ -193,6 +196,7 @@ public: READWRITE(nCacheCollateralBlock); READWRITE(nBlockLastPaid); READWRITE(nProtocolVersion); + READWRITE(nPoSeBanScore); READWRITE(fAllowMixingTx); READWRITE(fUnitTest); READWRITE(mapGovernanceObjectsVotedOn); @@ -220,6 +224,7 @@ public: swap(first.nCacheCollateralBlock, second.nCacheCollateralBlock); swap(first.nBlockLastPaid, second.nBlockLastPaid); swap(first.nProtocolVersion, second.nProtocolVersion); + swap(first.nPoSeBanScore, second.nPoSeBanScore); swap(first.fAllowMixingTx, second.fAllowMixingTx); swap(first.fUnitTest, second.fUnitTest); swap(first.mapGovernanceObjectsVotedOn, second.mapGovernanceObjectsVotedOn); @@ -246,6 +251,8 @@ public: bool IsEnabled() { return nActiveState == MASTERNODE_ENABLED; } bool IsPreEnabled() { return nActiveState == MASTERNODE_PRE_ENABLED; } + bool IsPoSeBanned() { return nActiveState == MASTERNODE_POSE_BAN; } + bool IsPoSeVerified() { return nPoSeBanScore <= -MASTERNODE_POSE_BAN_MAX_SCORE; } bool IsWatchdogExpired() { return nActiveState == MASTERNODE_WATCHDOG_EXPIRED; } @@ -339,12 +346,75 @@ public: static bool Create(CTxIn vin, CService service, CKey keyCollateralAddressNew, CPubKey pubKeyCollateralAddressNew, CKey keyMasternodeNew, CPubKey pubKeyMasternodeNew, std::string &strErrorRet, CMasternodeBroadcast &mnbRet); static bool Create(std::string strService, std::string strKey, std::string strTxHash, std::string strOutputIndex, std::string& strErrorRet, CMasternodeBroadcast &mnbRet, bool fOffline = false); - bool CheckAndUpdate(int& nDos); - bool CheckInputsAndAdd(int& nDos); + bool SimpleCheck(int& nDos); + bool Update(CMasternode* pmn, int& nDos); + bool CheckOutpoint(int& nDos); bool Sign(CKey& keyCollateralAddress); bool CheckSignature(int& nDos); void Relay(); }; +class CMasternodeVerification +{ +public: + CTxIn vin1; + CTxIn vin2; + CService addr; + int nonce; + int nBlockHeight; + std::vector vchSig1; + std::vector vchSig2; + + CMasternodeVerification() : + vin1(), + vin2(), + addr(), + nonce(0), + nBlockHeight(0), + vchSig1(), + vchSig2() + {} + + CMasternodeVerification(CService addr, int nonce, int nBlockHeight) : + vin1(), + vin2(), + addr(addr), + nonce(nonce), + nBlockHeight(nBlockHeight), + vchSig1(), + vchSig2() + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(vin1); + READWRITE(vin2); + READWRITE(addr); + READWRITE(nonce); + READWRITE(nBlockHeight); + READWRITE(vchSig1); + READWRITE(vchSig2); + } + + uint256 GetHash() const + { + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << vin1; + ss << vin2; + ss << addr; + ss << nonce; + ss << nBlockHeight; + return ss.GetHash(); + } + + void Relay() const + { + CInv inv(MSG_MASTERNODE_VERIFY, GetHash()); + RelayInv(inv); + } +}; + #endif diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 4819c508f..8e4ab24db 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -8,6 +8,7 @@ #include "masternode.h" #include "masternode-payments.h" #include "masternode-sync.h" +#include "netfulfilledman.h" #include "util.h" #include "addrman.h" #include "spork.h" @@ -17,6 +18,8 @@ /** Masternode manager */ CMasternodeMan mnodeman; +const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-1"; + struct CompareLastPaidBlock { bool operator()(const std::pair& t1, @@ -35,12 +38,14 @@ struct CompareScoreMN } }; -const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-1"; - -CMasternodeMan::CMasternodeMan() { - nDsqCount = 0; - nLastWatchdogVoteTime = 0; -} +struct CompareByAddr +{ + bool operator()(const CMasternode* t1, + const CMasternode* t2) const + { + return t1->addr < t2->addr; + } +}; bool CMasternodeMan::Add(CMasternode &mn) { @@ -151,6 +156,15 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval) } } + std::map::iterator itv1 = mWeAskedForVerification.begin(); + while(itv1 != mWeAskedForVerification.end()){ + if(itv1->second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) { + mWeAskedForVerification.erase(itv1++); + } else { + ++itv1; + } + } + // remove expired mapSeenMasternodeBroadcast map::iterator it3 = mapSeenMasternodeBroadcast.begin(); while(it3 != mapSeenMasternodeBroadcast.end()){ @@ -173,6 +187,16 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval) } } + // remove expired mapSeenMasternodeVerification + std::map::iterator itv2 = mapSeenMasternodeVerification.begin(); + while(itv2 != mapSeenMasternodeVerification.end()){ + if((*itv2).second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS){ + LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode verification: hash=%s\n", (*itv2).first.ToString()); + mapSeenMasternodeVerification.erase(itv2++); + } else { + ++itv2; + } + } } void CMasternodeMan::Clear() @@ -250,7 +274,7 @@ void CMasternodeMan::DsegUpdate(CNode* pnode) } pnode->PushMessage(NetMsgType::DSEG, CTxIn()); - int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; + int64_t askAgain = GetTime() + DSEG_UPDATE_SECONDS; mWeAskedForMasternodeList[pnode->addr] = askAgain; } @@ -670,7 +694,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData return; } } - int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; + int64_t askAgain = GetTime() + DSEG_UPDATE_SECONDS; mAskedUsForMasternodeList[pfrom->addr] = askAgain; } } //else, asking for a specific node which is ok @@ -705,6 +729,420 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData } // smth weird happen - someone asked us for vin we have no idea about? LogPrint("masternode", "CMasternodeMan::ProcessMessage -- DSEG -- No invs sent to peer %d\n", pfrom->id); + + } else if (strCommand == NetMsgType::MNVERIFY) { // Masternode Verify + + CMasternodeVerification mnv; + vRecv >> mnv; + + if(mnv.vchSig1.empty()) { + // CASE 1: someone asked me to verify myself /IP we are using/ + SendVerifyReply(pfrom, mnv); + } else if (mnv.vchSig2.empty()) { + // CASE 2: we _probably_ got verification we requested from some masternode + ProcessVerifyReply(pfrom, mnv); + } else { + // CASE 3: we _probably_ got verification broadcast signed by some masternode which verified another one + ProcessVerifyBroadcast(pfrom, mnv); + } + } +} + +void CMasternodeMan::DoFullVerificationStep() +{ + if(activeMasternode.vin == CTxIn()) return; + + std::vector > vecMasternodeRanks = GetMasternodeRanks(pCurrentBlockIndex->nHeight - 1, MIN_POSE_PROTO_VERSION); + + LOCK(cs); + + int nCount = 0; + int nCountMax = std::max(10, (int)vMasternodes.size() / 100); // verify at least 10 masternode at once but at most 1% of all known masternodes + + int nMyRank = -1; + int nRanksTotal = (int)vecMasternodeRanks.size(); + + // send verify requests only if we are in top MAX_POSE_RANK + std::vector >::iterator it = vecMasternodeRanks.begin(); + while(it != vecMasternodeRanks.end()) { + if(it->first > MAX_POSE_RANK) { + LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Must be in top %d to send verify request\n", + (int)MAX_POSE_RANK); + return; + } + if(it->second.vin == activeMasternode.vin) { + nMyRank = it->first; + LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Found self at rank %d/%d, verifying up to %d masternodes\n", + nMyRank, nRanksTotal, nCountMax); + break; + } + ++it; + } + + // edge case: list is too short and this masternode is not enabled + if(nMyRank == -1) return; + + // send verify requests to up to nCountMax masternodes starting from + // (MAX_POSE_RANK + nCountMax * (nMyRank - 1) + 1) + int nOffset = MAX_POSE_RANK + nCountMax * (nMyRank - 1); + if(nOffset >= (int)vecMasternodeRanks.size()) return; + + std::vector vSortedByAddr; + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + vSortedByAddr.push_back(&mn); + } + + sort(vSortedByAddr.begin(), vSortedByAddr.end(), CompareByAddr()); + + it = vecMasternodeRanks.begin() + nOffset; + while(it != vecMasternodeRanks.end()) { + if(it->second.IsPoSeVerified() || it->second.IsPoSeBanned()) { + LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Already %s%s%s masternode %s address %s, skipping...\n", + it->second.IsPoSeVerified() ? "verified" : "", + it->second.IsPoSeVerified() && it->second.IsPoSeBanned() ? " and " : "", + it->second.IsPoSeBanned() ? "banned" : "", + it->second.vin.prevout.ToStringShort(), it->second.addr.ToString()); + ++it; + continue; + } + LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Verifying masternode %s rank %d/%d address %s\n", + it->second.vin.prevout.ToStringShort(), it->first, nRanksTotal, it->second.addr.ToString()); + if(SendVerifyRequest((CAddress)it->second.addr, vSortedByAddr)) { + nCount++; + if(nCount >= nCountMax) break; + } + ++it; + } + + LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Sent verification requests to %d masternodes\n", nCount); +} + +void CMasternodeMan::CheckSameAddr() +{ + if(!masternodeSync.IsSynced() || vMasternodes.empty()) return; + + std::vector vBan; + std::vector vSortedByAddr; + + { + LOCK(cs); + + CMasternode* pprevMasternode = NULL; + CMasternode* pverifiedMasternode = NULL; + + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + vSortedByAddr.push_back(&mn); + } + + sort(vSortedByAddr.begin(), vSortedByAddr.end(), CompareByAddr()); + + BOOST_FOREACH(CMasternode* pmn, vSortedByAddr) { + // check only (pre)enabled masternodes + if(!pmn->IsEnabled() && !pmn->IsPreEnabled()) continue; + // initial step + if(!pprevMasternode) { + pprevMasternode = pmn; + pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; + continue; + } + // second+ step + if(pmn->addr == pprevMasternode->addr) { + if(pverifiedMasternode) { + // another masternode with the same ip is verified, ban this one + vBan.push_back(pmn); + } else if(pmn->IsPoSeVerified()) { + // this masternode with the same ip is verified, ban previous one + vBan.push_back(pprevMasternode); + // and keep a reference to be able to ban following masternodes with the same ip + pverifiedMasternode = pmn; + } + } else { + pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; + } + pprevMasternode = pmn; + } + } + + // ban duplicates + BOOST_FOREACH(CMasternode* pmn, vBan) { + pmn->nPoSeBanScore++; + } +} + +bool CMasternodeMan::SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr) +{ + if(netfulfilledman.HasFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { + // we already asked for verification, not a good idea to do this too often, skip it + LogPrint("masternode", "CMasternodeMan::SendVerifyRequest -- too many requests, skipping... addr=%s\n", addr.ToString()); + return false; + } + + CNode* pnode = ConnectNode(addr, NULL, true); + if(pnode != NULL) { + netfulfilledman.AddFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request"); + // use random nonce, store it and require node to reply with correct one later + CMasternodeVerification mnv(addr, GetInsecureRand(999999), pCurrentBlockIndex->nHeight - 1); + mWeAskedForVerification[addr] = mnv; + LogPrintf("CMasternodeMan::SendVerifyRequest -- verifying using nonce %d addr=%s\n", mnv.nonce, addr.ToString()); + pnode->PushMessage(NetMsgType::MNVERIFY, mnv); + return true; + } else { + // can't connect, add some PoSe "ban score" to all masternodes with given addr + bool fFound = false; + BOOST_FOREACH(CMasternode* pmn, vSortedByAddr) { + if(pmn->addr != addr) { + if(fFound) break; + continue; + } + fFound = true; + pmn->nPoSeBanScore++; + } + return false; + } +} + +void CMasternodeMan::SendVerifyReply(CNode* pnode, CMasternodeVerification& mnv) +{ + // only masternodes can sign this, why would someone ask regular node? + if(!fMasterNode) { + // do not ban, malicious node might be using my IP + // and trying to confuse the node which tries to verify it + return; + } + + if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply")) { + // peer should not ask us that often + LogPrintf("MasternodeMan::SendVerifyReply -- ERROR: peer already asked me recently, peer=%d\n", pnode->id); + Misbehaving(pnode->id, 20); + return; + } + + uint256 blockHash; + if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { + LogPrintf("MasternodeMan::SendVerifyReply -- can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); + return; + } + + std::string strMessage = strprintf("%s%d%s", activeMasternode.service.ToString(false), mnv.nonce, blockHash.ToString()); + + if(!darkSendSigner.SignMessage(strMessage, mnv.vchSig1, activeMasternode.keyMasternode)) { + LogPrintf("MasternodeMan::SendVerifyReply -- SignMessage() failed\n"); + return; + } + + std::string strError; + + if(!darkSendSigner.VerifyMessage(activeMasternode.pubKeyMasternode, mnv.vchSig1, strMessage, strError)) { + LogPrintf("MasternodeMan::SendVerifyReply -- VerifyMessage() failed, error: %s\n", strError); + return; + } + + pnode->PushMessage(NetMsgType::MNVERIFY, mnv); + netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply"); +} + +void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& mnv) +{ + std::string strError; + + // did we even ask for it? if that's the case we should have matching fulfilled request + if(!netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { + LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: we didn't ask for verification of %s, peer=%d\n", pnode->addr.ToString(), pnode->id); + Misbehaving(pnode->id, 20); + return; + } + + // Received nonce for a known address must match the one we sent + if(mWeAskedForVerification[pnode->addr].nonce != mnv.nonce) { + LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: wrong nounce: requested=%d, received=%d, peer=%d\n", + mWeAskedForVerification[pnode->addr].nonce, mnv.nonce, pnode->id); + Misbehaving(pnode->id, 20); + return; + } + + // Received nBlockHeight for a known address must match the one we sent + if(mWeAskedForVerification[pnode->addr].nBlockHeight != mnv.nBlockHeight) { + LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: wrong nBlockHeight: requested=%d, received=%d, peer=%d\n", + mWeAskedForVerification[pnode->addr].nBlockHeight, mnv.nBlockHeight, pnode->id); + Misbehaving(pnode->id, 20); + return; + } + + uint256 blockHash; + if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { + // this shouldn't happen... + LogPrintf("MasternodeMan::ProcessVerifyReply -- can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); + return; + } + + // we already verified this address, why node is spamming? + if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done")) { + LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: already verified %s recently\n", pnode->addr.ToString()); + Misbehaving(pnode->id, 20); + return; + } + + { + LOCK(cs); + + CMasternode* prealMasternode = NULL; + std::vector vpMasternodesToBan; + std::vector::iterator it = vMasternodes.begin(); + std::string strMessage1 = strprintf("%s%d%s", pnode->addr.ToString(false), mnv.nonce, blockHash.ToString()); + while(it != vMasternodes.end()) { + if((CAddress)it->addr == pnode->addr) { + if(darkSendSigner.VerifyMessage(it->pubKeyMasternode, mnv.vchSig1, strMessage1, strError)) { + // found it! + prealMasternode = &(*it); + if(!it->IsPoSeVerified()) { + it->nPoSeBanScore--; + } + netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done"); + + // we can only broadcast it if we are an activated masternode + if(activeMasternode.vin == CTxIn()) continue; + // update ... + mnv.addr = it->addr; + mnv.vin1 = it->vin; + mnv.vin2 = activeMasternode.vin; + std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString(), + mnv.vin1.prevout.ToStringShort(), mnv.vin2.prevout.ToStringShort()); + // ... and sign it + if(!darkSendSigner.SignMessage(strMessage2, mnv.vchSig2, activeMasternode.keyMasternode)) { + LogPrintf("MasternodeMan::ProcessVerifyReply -- SignMessage() failed\n"); + return; + } + + std::string strError; + + if(!darkSendSigner.VerifyMessage(activeMasternode.pubKeyMasternode, mnv.vchSig2, strMessage2, strError)) { + LogPrintf("MasternodeMan::ProcessVerifyReply -- VerifyMessage() failed, error: %s\n", strError); + return; + } + + mWeAskedForVerification[pnode->addr] = mnv; + mnv.Relay(); + + } else { + vpMasternodesToBan.push_back(&(*it)); + } + } + ++it; + } + // no real masternode found?... + if(!prealMasternode) { + // this should never be the case normally, + // only if someone is trying to game the system in some way or smth like that + LogPrintf("CMasternodeMan::ProcessVerifyReply -- ERROR: no real masternode found for addr %s\n", pnode->addr.ToString()); + Misbehaving(pnode->id, 20); + return; + } + LogPrintf("CMasternodeMan::ProcessVerifyReply -- verified real masternode %s for addr %s\n", + prealMasternode->vin.prevout.ToStringShort(), pnode->addr.ToString()); + // increase ban score for everyone else + BOOST_FOREACH(CMasternode* pmn, vpMasternodesToBan) { + pmn->nPoSeBanScore++; + LogPrint("masternode", "CMasternodeMan::ProcessVerifyBroadcast -- increased PoSe ban score for %s addr %s, new score %d\n", + prealMasternode->vin.prevout.ToStringShort(), pnode->addr.ToString(), pmn->nPoSeBanScore); + } + LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- PoSe score incresed for %d fake masternodes, addr %s\n", + (int)vpMasternodesToBan.size(), pnode->addr.ToString()); + } +} + +void CMasternodeMan::ProcessVerifyBroadcast(CNode* pnode, const CMasternodeVerification& mnv) +{ + std::string strError; + + if(mapSeenMasternodeVerification.find(mnv.GetHash()) != mapSeenMasternodeVerification.end()) { + // we already have one + return; + } + mapSeenMasternodeVerification[mnv.GetHash()] = mnv; + + // we don't care about history + if(mnv.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) { + LogPrint("masternode", "MasternodeMan::ProcessVerifyBroadcast -- Outdated: current block %d, verification block %d, peer=%d\n", + pCurrentBlockIndex->nHeight, mnv.nBlockHeight, pnode->id); + return; + } + + if(mnv.vin1.prevout == mnv.vin2.prevout) { + LogPrint("masternode", "MasternodeMan::ProcessVerifyBroadcast -- ERROR: same vins %s, peer=%d\n", + mnv.vin1.prevout.ToStringShort(), pnode->id); + // that was NOT a good idea to cheat and verify itself, + // ban the node we received such message from + Misbehaving(pnode->id, 100); + return; + } + + uint256 blockHash; + if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { + // this shouldn't happen... + LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- Can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); + return; + } + + int nRank = GetMasternodeRank(mnv.vin2, mnv.nBlockHeight, MIN_POSE_PROTO_VERSION); + if(nRank < MAX_POSE_RANK) { + LogPrint("masternode", "MasternodeMan::ProcessVerifyBroadcast -- Mastrernode is not in top %d, current rank %d, peer=%d\n", + (int)MAX_POSE_RANK, nRank, pnode->id); + return; + } + + { + LOCK(cs); + + std::string strMessage1 = strprintf("%s%d%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString()); + std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString(), + mnv.vin1.prevout.ToStringShort(), mnv.vin2.prevout.ToStringShort()); + + CMasternode* pmn1 = Find(mnv.vin1); + if(!pmn1) { + LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- can't find masternode1 %s\n", mnv.vin1.prevout.ToStringShort()); + return; + } + + CMasternode* pmn2 = Find(mnv.vin2); + if(!pmn2) { + LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- can't find masternode2 %s\n", mnv.vin2.prevout.ToStringShort()); + return; + } + + if(pmn1->addr != mnv.addr) { + LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- addr %s do not match %s\n", mnv.addr.ToString(), pnode->addr.ToString()); + return; + } + + if(darkSendSigner.VerifyMessage(pmn1->pubKeyMasternode, mnv.vchSig1, strMessage1, strError)) { + LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- VerifyMessage() for masternode1 failed, error: %s\n", strError); + return; + } + + if(darkSendSigner.VerifyMessage(pmn2->pubKeyMasternode, mnv.vchSig2, strMessage2, strError)) { + LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- VerifyMessage() for masternode2 failed, error: %s\n", strError); + return; + } + + if(!pmn1->IsPoSeVerified()) { + pmn1->nPoSeBanScore--; + } + mnv.Relay(); + + LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- verified masternode %s for addr %s\n", + pmn1->vin.prevout.ToStringShort(), pnode->addr.ToString()); + + // increase ban score for everyone else with the same addr + int nCount = 0; + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + if(mn.addr != mnv.addr || mn.vin.prevout == mnv.vin1.prevout) continue; + mn.nPoSeBanScore++; + nCount++; + LogPrint("masternode", "CMasternodeMan::ProcessVerifyBroadcast -- increased PoSe ban score for %s addr %s, new score %d\n", + mn.vin.prevout.ToStringShort(), mn.addr.ToString(), mn.nPoSeBanScore); + } + LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- PoSe score incresed for %d fake masternodes, addr %s\n", + nCount, pnode->addr.ToString()); } } @@ -778,49 +1216,71 @@ void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast mnb) { } } -bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, int& nDos) { +bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, int& nDos) +{ LOCK(cs); + nDos = 0; - LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList - Masternode broadcast, vin: %s\n", mnb.vin.ToString()); + LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- masternode=%s\n", mnb.vin.prevout.ToStringShort()); if(mapSeenMasternodeBroadcast.count(mnb.GetHash())) { //seen return true; } - mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb)); + mapSeenMasternodeBroadcast.insert(std::make_pair(mnb.GetHash(), mnb)); - LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList - Masternode broadcast, vin: %s new\n", mnb.vin.ToString()); - // We check addr before both initial mnb and update - if(!mnb.IsValidNetAddr()) { - LogPrintf("CMasternodeBroadcast::CheckMnbAndUpdateMasternodeList -- Invalid addr, rejected: masternode=%s sigTime=%lld addr=%s\n", - mnb.vin.prevout.ToStringShort(), mnb.sigTime, mnb.addr.ToString()); + LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- masternode=%s new\n", mnb.vin.prevout.ToStringShort()); + + if(!mnb.SimpleCheck(nDos)) { + LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- SimpleCheck() failed, masternode=%s\n", mnb.vin.prevout.ToStringShort()); return false; } - if(!mnb.CheckAndUpdate(nDos)){ - LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList - Masternode broadcast, vin: %s CheckAndUpdate failed\n", mnb.vin.ToString()); - return false; - } - - // make sure it's still unspent - // - this is checked later by .check() in many places and by ThreadCheckDarkSendPool() - if(mnb.CheckInputsAndAdd(nDos)) { - masternodeSync.AddedMasternodeList(); + // search Masternode list + CMasternode* pmn = Find(mnb.vin); + if(pmn) { + if(!mnb.Update(pmn, nDos)) { + LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList -- Update() failed, masternode=%s\n", mnb.vin.prevout.ToStringShort()); + return false; + } } else { - LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList - Rejected Masternode entry %s\n", mnb.addr.ToString()); - return false; + if(mnb.CheckOutpoint(nDos)) { + Add(mnb); + masternodeSync.AddedMasternodeList(); + // if it matches our Masternode privkey... + if(fMasterNode && mnb.pubKeyMasternode == activeMasternode.pubKeyMasternode) { + mnb.nPoSeBanScore = -MASTERNODE_POSE_BAN_MAX_SCORE; + if(mnb.nProtocolVersion == PROTOCOL_VERSION) { + // ... and PROTOCOL_VERSION, then we've been remotely activated ... + LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList -- Got NEW Masternode entry: masternode=%s sigTime=%lld addr=%s\n", + mnb.vin.prevout.ToStringShort(), mnb.sigTime, mnb.addr.ToString()); + 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("CMasternodeMan::CheckMnbAndUpdateMasternodeList -- wrong PROTOCOL_VERSION, re-activate your MN: message nProtocolVersion=%d PROTOCOL_VERSION=%d\n", mnb.nProtocolVersion, PROTOCOL_VERSION); + return false; + } + } + mnb.Relay(); + } else { + LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList -- Rejected Masternode entry: %s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); + return false; + } } return true; } -void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex) { +void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex) +{ LOCK(cs); + if(fLiteMode) return; static bool IsFirstRun = true; // Do full scan on first run or if we are not a masternode // (MNs should update this info on every block, so limited scan should be enough for them) - int nMaxBlocksToScanBack = (IsFirstRun || !fMasterNode) ? mnpayments.GetStorageLimit() : MASTERNODES_LAST_PAID_SCAN_BLOCKS; + int nMaxBlocksToScanBack = (IsFirstRun || !fMasterNode) ? mnpayments.GetStorageLimit() : LAST_PAID_SCAN_BLOCKS; // LogPrint("mnpayments", "CMasternodeMan::UpdateLastPaid -- nHeight=%d, nMaxBlocksToScanBack=%d, IsFirstRun=%s\n", // pindex->nHeight, nMaxBlocksToScanBack, IsFirstRun ? "true" : "false"); @@ -935,3 +1395,17 @@ void CMasternodeMan::SetMasternodeLastPing(const CTxIn& vin, const CMasternodePi mapSeenMasternodeBroadcast[hash].lastPing = mnp; } } + +void CMasternodeMan::UpdatedBlockTip(const CBlockIndex *pindex) +{ + pCurrentBlockIndex = pindex; + LogPrint("masternode", "CMasternodeMan::UpdatedBlockTip -- pCurrentBlockIndex->nHeight=%d\n", pCurrentBlockIndex->nHeight); + + CheckSameAddr(); + + if(fMasterNode) { + DoFullVerificationStep(); + // normal wallet does not need to update this every block, doing update on rpc call should be enough + UpdateLastPaid(pindex); + } +} diff --git a/src/masternodeman.h b/src/masternodeman.h index 3178f2308..b3cac697e 100644 --- a/src/masternodeman.h +++ b/src/masternodeman.h @@ -5,20 +5,13 @@ #ifndef MASTERNODEMAN_H #define MASTERNODEMAN_H -#include "sync.h" -#include "net.h" -#include "key.h" -#include "util.h" -#include "base58.h" -#include "main.h" #include "masternode.h" - -#define MASTERNODES_DUMP_SECONDS (15*60) -#define MASTERNODES_DSEG_SECONDS (3*60*60) +#include "sync.h" using namespace std; class CMasternodeMan; + extern CMasternodeMan mnodeman; class CMasternodeMan @@ -26,13 +19,20 @@ class CMasternodeMan private: static const std::string SERIALIZATION_VERSION_STRING; - static const int MASTERNODES_LAST_PAID_SCAN_BLOCKS = 100; + static const int DSEG_UPDATE_SECONDS = 3 * 60 * 60; + + static const int LAST_PAID_SCAN_BLOCKS = 100; + + static const int MIN_POSE_PROTO_VERSION = 70203; + static const int MAX_POSE_RANK = 10; + static const int MAX_POSE_BLOCKS = 10; + // critical section to protect the inner data structures mutable CCriticalSection cs; - // critical section to protect the inner data structures specifically on messaging - mutable CCriticalSection cs_process_message; + // Keep track of current block index + const CBlockIndex *pCurrentBlockIndex; // map to hold all MNs std::vector vMasternodes; @@ -42,6 +42,8 @@ private: std::map mWeAskedForMasternodeList; // which Masternodes we've asked for std::map mWeAskedForMasternodeListEntry; + // who we asked for the masternode verification + std::map mWeAskedForVerification; std::vector vecDirtyGovernanceObjectHashes; @@ -49,16 +51,19 @@ private: public: // Keep track of all broadcasts I've seen - map mapSeenMasternodeBroadcast; + std::map mapSeenMasternodeBroadcast; // Keep track of all pings I've seen - map mapSeenMasternodePing; - + std::map mapSeenMasternodePing; + // Keep track of all verifications I've seen + std::map mapSeenMasternodeVerification; // keep track of dsq count to prevent masternodes from gaming darksend queue int64_t nDsqCount; // dummy script pubkey to test masternodes' vins against mempool CScript dummyScriptPubkey; + CMasternodeMan() : nLastWatchdogVoteTime(0), nDsqCount(0) {} + ADD_SERIALIZE_METHODS; template @@ -83,14 +88,10 @@ public: READWRITE(mapSeenMasternodeBroadcast); READWRITE(mapSeenMasternodePing); if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { - LogPrintf("CMasternodeMan::SerializationOp - Incompatible format detected, resetting data\n"); Clear(); } } - CMasternodeMan(); - CMasternodeMan(CMasternodeMan& other); - /// Add an entry bool Add(CMasternode &mn); @@ -148,6 +149,13 @@ public: void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + void DoFullVerificationStep(); + void CheckSameAddr(); + bool SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr); + void SendVerifyReply(CNode* pnode, CMasternodeVerification& mnv); + void ProcessVerifyReply(CNode* pnode, CMasternodeVerification& mnv); + void ProcessVerifyBroadcast(CNode* pnode, const CMasternodeVerification& mnv); + /// Return the number of (unique) Masternodes int size() { return vMasternodes.size(); } @@ -179,24 +187,20 @@ public: } bool IsWatchdogActive(); - void UpdateWatchdogVoteTime(const CTxIn& vin); - void AddGovernanceVote(const CTxIn& vin, uint256 nGovernanceObjectHash); - void RemoveGovernanceObject(uint256 nGovernanceObjectHash); void CheckMasternode(const CTxIn& vin, bool fForce = false); - void CheckMasternode(const CPubKey& pubKeyMasternode, bool fForce = false); int GetMasternodeState(const CTxIn& vin); - int GetMasternodeState(const CPubKey& pubKeyMasternode); bool IsMasternodePingedWithin(const CTxIn& vin, int nSeconds, int64_t nTimeToCheckAt = -1); - void SetMasternodeLastPing(const CTxIn& vin, const CMasternodePing& mnp); + + void UpdatedBlockTip(const CBlockIndex *pindex); }; #endif diff --git a/src/protocol.cpp b/src/protocol.cpp index 4cc640a52..34d7a3bb6 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -65,6 +65,7 @@ const char *SYNCSTATUSCOUNT="ssc"; const char *MNGOVERNANCESYNC="govsync"; const char *MNGOVERNANCEOBJECT="govobj"; const char *MNGOVERNANCEOBJECTVOTE="govobjvote"; +const char *MNVERIFY="mnv"; }; static const char* ppszTypeName[] = @@ -89,7 +90,8 @@ static const char* ppszTypeName[] = NetMsgType::MNPING, NetMsgType::DSTX, NetMsgType::MNGOVERNANCEOBJECT, - NetMsgType::MNGOVERNANCEOBJECTVOTE + NetMsgType::MNGOVERNANCEOBJECTVOTE, + NetMsgType::MNVERIFY, }; /** All known message types. Keep this in the same order as the list of @@ -141,7 +143,8 @@ const static std::string allNetMessageTypes[] = { NetMsgType::SYNCSTATUSCOUNT, NetMsgType::MNGOVERNANCESYNC, NetMsgType::MNGOVERNANCEOBJECT, - NetMsgType::MNGOVERNANCEOBJECTVOTE + NetMsgType::MNGOVERNANCEOBJECTVOTE, + NetMsgType::MNVERIFY, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index 868e3924d..04b2931ef 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -243,6 +243,7 @@ extern const char *SYNCSTATUSCOUNT; extern const char *MNGOVERNANCESYNC; extern const char *MNGOVERNANCEOBJECT; extern const char *MNGOVERNANCEOBJECTVOTE; +extern const char *MNVERIFY; }; /* Get a vector of all valid message types (see above) */ @@ -356,7 +357,8 @@ enum { MSG_MASTERNODE_PING, MSG_DSTX, MSG_GOVERNANCE_OBJECT, - MSG_GOVERNANCE_OBJECT_VOTE + MSG_GOVERNANCE_OBJECT_VOTE, + MSG_MASTERNODE_VERIFY, }; #endif // BITCOIN_PROTOCOL_H