// Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2019 The PIVX developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // clang-format off #include "main.h" #include "activemasternode.h" #include "masternode-sync.h" #include "masternode-payments.h" #include "masternode-budget.h" #include "masternode.h" #include "masternodeman.h" #include "spork.h" #include "util.h" #include "addrman.h" // clang-format on class CMasternodeSync; CMasternodeSync masternodeSync; CMasternodeSync::CMasternodeSync() { Reset(); } bool CMasternodeSync::IsSynced() { return RequestedMasternodeAssets == MASTERNODE_SYNC_FINISHED; } bool CMasternodeSync::IsBlockchainSynced() { static bool fBlockchainSynced = false; static int64_t lastProcess = GetTime(); // if the last call to this function was more than 10 years ago (client was in sleep mode) reset the sync process if (GetTime() - lastProcess > 60 * 60 * 24 * 365 * 10) { Reset(); fBlockchainSynced = false; } lastProcess = GetTime(); if (fBlockchainSynced) return true; if (fImporting || fReindex) return false; TRY_LOCK(cs_main, lockMain); if (!lockMain) return false; CBlockIndex* pindex = chainActive.Tip(); if (pindex == NULL) return false; if (pindex->nTime + 60 * 60 * 24 * 365 * 10 < GetTime()) return false; fBlockchainSynced = true; return true; } void CMasternodeSync::Reset() { lastMasternodeList = 0; lastMasternodeWinner = 0; lastBudgetItem = 0; mapSeenSyncMNB.clear(); mapSeenSyncMNW.clear(); mapSeenSyncBudget.clear(); lastFailure = 0; nCountFailures = 0; sumMasternodeList = 0; sumMasternodeWinner = 0; sumBudgetItemProp = 0; sumBudgetItemFin = 0; countMasternodeList = 0; countMasternodeWinner = 0; countBudgetItemProp = 0; countBudgetItemFin = 0; RequestedMasternodeAssets = MASTERNODE_SYNC_INITIAL; RequestedMasternodeAttempt = 0; nAssetSyncStarted = GetTime(); } void CMasternodeSync::AddedMasternodeList(uint256 hash) { if (mnodeman.mapSeenMasternodeBroadcast.count(hash)) { if (mapSeenSyncMNB[hash] < MASTERNODE_SYNC_THRESHOLD) { lastMasternodeList = GetTime(); mapSeenSyncMNB[hash]++; } } else { lastMasternodeList = GetTime(); mapSeenSyncMNB.insert(make_pair(hash, 1)); } } void CMasternodeSync::AddedMasternodeWinner(uint256 hash) { if (masternodePayments.mapMasternodePayeeVotes.count(hash)) { if (mapSeenSyncMNW[hash] < MASTERNODE_SYNC_THRESHOLD) { lastMasternodeWinner = GetTime(); mapSeenSyncMNW[hash]++; } } else { lastMasternodeWinner = GetTime(); mapSeenSyncMNW.insert(make_pair(hash, 1)); } } void CMasternodeSync::AddedBudgetItem(uint256 hash) { if (budget.mapSeenMasternodeBudgetProposals.count(hash) || budget.mapSeenMasternodeBudgetVotes.count(hash) || budget.mapSeenFinalizedBudgets.count(hash) || budget.mapSeenFinalizedBudgetVotes.count(hash)) { if (mapSeenSyncBudget[hash] < MASTERNODE_SYNC_THRESHOLD) { lastBudgetItem = GetTime(); mapSeenSyncBudget[hash]++; } } else { lastBudgetItem = GetTime(); mapSeenSyncBudget.insert(make_pair(hash, 1)); } } bool CMasternodeSync::IsBudgetPropEmpty() { return sumBudgetItemProp == 0 && countBudgetItemProp > 0; } bool CMasternodeSync::IsBudgetFinEmpty() { return sumBudgetItemFin == 0 && countBudgetItemFin > 0; } void CMasternodeSync::GetNextAsset() { switch (RequestedMasternodeAssets) { case (MASTERNODE_SYNC_INITIAL): case (MASTERNODE_SYNC_FAILED): // should never be used here actually, use Reset() instead ClearFulfilledRequest(); RequestedMasternodeAssets = MASTERNODE_SYNC_SPORKS; break; case (MASTERNODE_SYNC_SPORKS): RequestedMasternodeAssets = MASTERNODE_SYNC_LIST; break; case (MASTERNODE_SYNC_LIST): RequestedMasternodeAssets = MASTERNODE_SYNC_MNW; break; case (MASTERNODE_SYNC_MNW): RequestedMasternodeAssets = MASTERNODE_SYNC_BUDGET; break; case (MASTERNODE_SYNC_BUDGET): LogPrintf("CMasternodeSync::GetNextAsset - Sync has finished\n"); RequestedMasternodeAssets = MASTERNODE_SYNC_FINISHED; break; } RequestedMasternodeAttempt = 0; nAssetSyncStarted = GetTime(); } std::string CMasternodeSync::GetSyncStatus() { switch (masternodeSync.RequestedMasternodeAssets) { case MASTERNODE_SYNC_INITIAL: return _("Synchronization pending..."); case MASTERNODE_SYNC_SPORKS: return _("Synchronizing sporks..."); case MASTERNODE_SYNC_LIST: return _("Synchronizing masternodes..."); case MASTERNODE_SYNC_MNW: return _("Synchronizing masternode winners..."); case MASTERNODE_SYNC_BUDGET: return _("Synchronizing budgets..."); case MASTERNODE_SYNC_FAILED: return _("Synchronization failed"); case MASTERNODE_SYNC_FINISHED: return _("Synchronization finished"); } return ""; } void CMasternodeSync::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if (strCommand == "ssc") { //Sync status count int nItemID; int nCount; vRecv >> nItemID >> nCount; if (RequestedMasternodeAssets >= MASTERNODE_SYNC_FINISHED) return; //this means we will receive no further communication switch (nItemID) { case (MASTERNODE_SYNC_LIST): if (nItemID != RequestedMasternodeAssets) return; sumMasternodeList += nCount; countMasternodeList++; break; case (MASTERNODE_SYNC_MNW): if (nItemID != RequestedMasternodeAssets) return; sumMasternodeWinner += nCount; countMasternodeWinner++; break; case (MASTERNODE_SYNC_BUDGET_PROP): if (RequestedMasternodeAssets != MASTERNODE_SYNC_BUDGET) return; sumBudgetItemProp += nCount; countBudgetItemProp++; break; case (MASTERNODE_SYNC_BUDGET_FIN): if (RequestedMasternodeAssets != MASTERNODE_SYNC_BUDGET) return; sumBudgetItemFin += nCount; countBudgetItemFin++; break; } LogPrint("masternode", "CMasternodeSync:ProcessMessage - ssc - got inventory count %d %d\n", nItemID, nCount); } } void CMasternodeSync::ClearFulfilledRequest() { TRY_LOCK(cs_vNodes, lockRecv); if (!lockRecv) return; for (CNode* pnode : vNodes) { pnode->ClearFulfilledRequest("getspork"); pnode->ClearFulfilledRequest("mnsync"); pnode->ClearFulfilledRequest("mnwsync"); pnode->ClearFulfilledRequest("busync"); } } void CMasternodeSync::Process() { static int tick = 0; if (tick++ % MASTERNODE_SYNC_TIMEOUT != 0) return; if (IsSynced()) { /* Resync if we lose all masternodes from sleep/wake or failure to sync originally */ if (mnodeman.CountEnabled() == 0 && IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { Reset(); } else return; } //try syncing again if (RequestedMasternodeAssets == MASTERNODE_SYNC_FAILED && lastFailure + (1 * 60) < GetTime()) { Reset(); } else if (RequestedMasternodeAssets == MASTERNODE_SYNC_FAILED) { return; } LogPrint("masternode", "CMasternodeSync::Process() - tick %d RequestedMasternodeAssets %d\n", tick, RequestedMasternodeAssets); if (RequestedMasternodeAssets == MASTERNODE_SYNC_INITIAL) GetNextAsset(); // sporks synced but blockchain is not, wait until we're almost at a recent block to continue if (Params().NetworkID() != CBaseChainParams::REGTEST && !IsBlockchainSynced() && RequestedMasternodeAssets > MASTERNODE_SYNC_SPORKS) return; // Small networks can have every connected peer marked fulfilled before an // empty masternode stage reaches its per-peer timeout check. Advance empty // stages here so staking is not blocked forever on networks with no // masternodes, winners, or budgets. if (GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5) { if (RequestedMasternodeAssets == MASTERNODE_SYNC_SPORKS) { GetNextAsset(); return; } if (RequestedMasternodeAssets == MASTERNODE_SYNC_LIST && lastMasternodeList == 0) { if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; RequestedMasternodeAttempt = 0; lastFailure = GetTime(); nCountFailures++; } else { GetNextAsset(); } return; } if (RequestedMasternodeAssets == MASTERNODE_SYNC_MNW && lastMasternodeWinner == 0) { if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; RequestedMasternodeAttempt = 0; lastFailure = GetTime(); nCountFailures++; } else { GetNextAsset(); } return; } if (RequestedMasternodeAssets == MASTERNODE_SYNC_BUDGET && lastBudgetItem == 0) { GetNextAsset(); activeMasternode.ManageStatus(); return; } } TRY_LOCK(cs_vNodes, lockRecv); if (!lockRecv) return; for (CNode* pnode : vNodes) { if (Params().NetworkID() == CBaseChainParams::REGTEST) { if (RequestedMasternodeAttempt <= 2) { pnode->PushMessage("getsporks"); //get current network sporks } else if (RequestedMasternodeAttempt < 4) { mnodeman.DsegUpdate(pnode); } else if (RequestedMasternodeAttempt < 6) { int nMnCount = mnodeman.CountEnabled(); pnode->PushMessage("mnget", nMnCount); //sync payees uint256 n = 0; pnode->PushMessage("mnvs", n); //sync masternode votes } else { RequestedMasternodeAssets = MASTERNODE_SYNC_FINISHED; } RequestedMasternodeAttempt++; return; } //set to synced if (RequestedMasternodeAssets == MASTERNODE_SYNC_SPORKS) { if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD) { GetNextAsset(); return; } if (pnode->HasFulfilledRequest("getspork")) continue; pnode->FulfilledRequest("getspork"); pnode->PushMessage("getsporks"); //get current network sporks RequestedMasternodeAttempt++; return; } if (pnode->nVersion >= masternodePayments.GetMinMasternodePaymentsProto()) { if (RequestedMasternodeAssets == MASTERNODE_SYNC_LIST) { LogPrint("masternode", "CMasternodeSync::Process() - lastMasternodeList %lld (GetTime() - MASTERNODE_SYNC_TIMEOUT) %lld\n", lastMasternodeList, GetTime() - MASTERNODE_SYNC_TIMEOUT); if (lastMasternodeList > 0 && lastMasternodeList < GetTime() - MASTERNODE_SYNC_TIMEOUT * 2 && RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD) { //hasn't received a new item in the last five seconds, so we'll move to the GetNextAsset(); return; } if (pnode->HasFulfilledRequest("mnsync")) continue; pnode->FulfilledRequest("mnsync"); // timeout if (lastMasternodeList == 0 && (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3 || GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5)) { if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; RequestedMasternodeAttempt = 0; lastFailure = GetTime(); nCountFailures++; } else { GetNextAsset(); } return; } if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) return; mnodeman.DsegUpdate(pnode); RequestedMasternodeAttempt++; return; } if (RequestedMasternodeAssets == MASTERNODE_SYNC_MNW) { if (lastMasternodeWinner > 0 && lastMasternodeWinner < GetTime() - MASTERNODE_SYNC_TIMEOUT * 2 && RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD) { //hasn't received a new item in the last five seconds, so we'll move to the GetNextAsset(); return; } if (pnode->HasFulfilledRequest("mnwsync")) continue; pnode->FulfilledRequest("mnwsync"); // timeout if (lastMasternodeWinner == 0 && (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3 || GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5)) { if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; RequestedMasternodeAttempt = 0; lastFailure = GetTime(); nCountFailures++; } else { GetNextAsset(); } return; } if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) return; CBlockIndex* pindexPrev = chainActive.Tip(); if (pindexPrev == NULL) return; int nMnCount = mnodeman.CountEnabled(); pnode->PushMessage("mnget", nMnCount); //sync payees RequestedMasternodeAttempt++; return; } } if (pnode->nVersion >= ActiveProtocol()) { if (RequestedMasternodeAssets == MASTERNODE_SYNC_BUDGET) { // We'll start rejecting votes if we accidentally get set as synced too soon if (lastBudgetItem > 0 && lastBudgetItem < GetTime() - MASTERNODE_SYNC_TIMEOUT * 2 && RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD) { // Hasn't received a new item in the last five seconds, so we'll move to the GetNextAsset(); // Try to activate our masternode if possible activeMasternode.ManageStatus(); return; } // timeout if (lastBudgetItem == 0 && (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3 || GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5)) { // maybe there is no budgets at all, so just finish syncing GetNextAsset(); activeMasternode.ManageStatus(); return; } if (pnode->HasFulfilledRequest("busync")) continue; pnode->FulfilledRequest("busync"); if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) return; uint256 n = 0; pnode->PushMessage("mnvs", n); //sync masternode votes RequestedMasternodeAttempt++; return; } } } }