From 94e38e318454865291c51a4e5281bec16fcab398 Mon Sep 17 00:00:00 2001 From: Tim Flynn Date: Mon, 17 Oct 2016 14:54:28 -0400 Subject: [PATCH] V0.12.1.x sentinel watchdog pr (#1079) Squashed: * Replaced unsafe mnodeman.Find function with Get in governance-vote.cpp * Reject unparsable governance objects * Implemented sentinel watchdog objects (separated out from locking changes) * Added WATCHDOG support to rpcgovernance.cpp * Implemented WATCHDOG_EXPIRED state for masternodes * Added serialization of watchdog timestamps * Masternode fixes - Added version check to CMasternodeMan deserialization - Added several missing locking calls in CMasternodeMan * Fixed missing member initialization in CMasternode constructor and added more logging * Added MASTERNODE_WATCHDOG_MAX_SECONDS to governanceinfo * Added masternodewatchdogmaxseconds info to getgovernanceinfo help * Make masternodes remain in WATCHDOG_EXPIRED state unless removed or collateral expires * Allow watchdog object creation by WATCHDOG_EXPIRED MN * Fixed MN validation logic for governance object creation * Count total masternodes instead of enabled masternodes in masternode-sync * Transition out of WATCHDOG_EXPIRED state if the watchdog is inactive * Fixed IsWatchdogExpired bug * Fixed rate check for watchdog objects and no longer check MN state when validating governance objects * Applied PR #1061 patch * Ported locking changes from other branch * Require only 1 block between new watchdog objects * Accept pings for WATCHDOG_EXPIRED masternodes * Lock CmasternodeMan::cs in CmasternodeMan::ProcessMessage * Several governance changes - Fixed uninitialized value in CGovernancePayment class - Return an error on submission if any superblock payment cannot be parsed - Added logging more statements * Explicitly initialize all governance object members * Fix deadlock * Fixed non-threadsafe access to masternode in activemasternode.cpp * Revert added wallet lock * Changed CActiveMasternode so that watchdog expired nodes can still send pings * Modified CActiveMasternode to run pinger regardless of state when MN is in list * Added voter and time information to getvotes command * Improved CActiveMasternode state management * Implemented GetInfo functions for more efficient thread-safe access to masternode information * Added CActiveMasternode debug logging messages * Fixed initial type setting and error message for incorrect protocol version * Changes based on code review comments * Set active state for local mode --- src/activemasternode.cpp | 331 ++++++++++++++++++++++--------------- src/activemasternode.h | 26 ++- src/governance-classes.cpp | 45 ++++- src/governance-classes.h | 30 ++-- src/governance-vote.cpp | 7 +- src/governance-vote.h | 14 +- src/governance.cpp | 185 ++++++++++++--------- src/governance.h | 3 +- src/masternode-sync.cpp | 8 +- src/masternode.cpp | 104 +++++++++--- src/masternode.h | 53 +++++- src/masternodeman.cpp | 161 +++++++++++++++++- src/masternodeman.h | 62 +++++++ src/rpcgovernance.cpp | 21 ++- 14 files changed, 774 insertions(+), 276 deletions(-) diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index ef81bd9c7..0df2739f7 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -13,125 +13,42 @@ extern CWallet* pwalletMain; // Keep track of the active Masternode CActiveMasternode activeMasternode; -// Bootup the Masternode, look for a 1000DASH input and register on the network void CActiveMasternode::ManageState() { - std::string strError; + LogPrint("masternode", "CActiveMasternode::ManageState -- Start\n"); + if(!fMasterNode) { + LogPrint("masternode", "CActiveMasternode::ManageState -- Not a masternode, returning\n"); + return; + } - if(!fMasterNode) return; - - if (fDebug) LogPrintf("CActiveMasternode::ManageState -- Begin\n"); - - //need correct blocks to send ping if(Params().NetworkIDString() != CBaseChainParams::REGTEST && !masternodeSync.IsBlockchainSynced()) { nState = ACTIVE_MASTERNODE_SYNC_IN_PROCESS; LogPrintf("CActiveMasternode::ManageState -- %s\n", GetStatus()); return; } - if(nState == ACTIVE_MASTERNODE_SYNC_IN_PROCESS) nState = ACTIVE_MASTERNODE_INITIAL; - - if(nState == ACTIVE_MASTERNODE_INITIAL) { - CMasternode *pmn; - pmn = mnodeman.Find(pubKeyMasternode); - if(pmn != NULL) { - pmn->Check(); - if((pmn->IsEnabled() || pmn->IsPreEnabled()) && pmn->nProtocolVersion == PROTOCOL_VERSION) - EnableRemoteMasterNode(pmn->vin, pmn->addr); - } + if(nState == ACTIVE_MASTERNODE_SYNC_IN_PROCESS) { + nState = ACTIVE_MASTERNODE_INITIAL; } - if(nState != ACTIVE_MASTERNODE_STARTED) { + LogPrint("masternode", "CActiveMasternode::ManageState -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetType(), fPingerEnabled); - // Set defaults - nState = ACTIVE_MASTERNODE_NOT_CAPABLE; - strNotCapableReason = ""; - - if(pwalletMain->IsLocked()) { - strNotCapableReason = "Wallet is locked."; - LogPrintf("CActiveMasternode::ManageState -- not capable: %s\n", strNotCapableReason); - return; - } - - if(pwalletMain->GetBalance() == 0) { - nState = ACTIVE_MASTERNODE_INITIAL; - LogPrintf("CActiveMasternode::ManageState() -- %s\n", GetStatus()); - return; - } - - if(!GetLocal(service)) { - strNotCapableReason = "Can't detect external address. Please consider using the externalip configuration option if problem persists."; - LogPrintf("CActiveMasternode::ManageState -- not capable: %s\n", strNotCapableReason); - return; - } - - int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); - if(Params().NetworkIDString() == CBaseChainParams::MAIN) { - if(service.GetPort() != mainnetDefaultPort) { - strNotCapableReason = strprintf("Invalid port: %u - only %d is supported on mainnet.", service.GetPort(), mainnetDefaultPort); - LogPrintf("CActiveMasternode::ManageState -- not capable: %s\n", strNotCapableReason); - return; - } - } else if(service.GetPort() == mainnetDefaultPort) { - strNotCapableReason = strprintf("Invalid port: %u - %d is only supported on mainnet.", service.GetPort(), mainnetDefaultPort); - LogPrintf("CActiveMasternode::ManageState -- not capable: %s\n", strNotCapableReason); - return; - } - - LogPrintf("CActiveMasternode::ManageState -- Checking inbound connection to '%s'\n", service.ToString()); - - if(!ConnectNode((CAddress)service, NULL, true)) { - strNotCapableReason = "Could not connect to " + service.ToString(); - LogPrintf("CActiveMasternode::ManageState -- not capable: %s\n", strNotCapableReason); - return; - } - - // Choose coins to use - CPubKey pubKeyCollateral; - CKey keyCollateral; - - if(pwalletMain->GetMasternodeVinAndKeys(vin, pubKeyCollateral, keyCollateral)) { - - int nInputAge = GetInputAge(vin); - if(nInputAge < Params().GetConsensus().nMasternodeMinimumConfirmations){ - nState = ACTIVE_MASTERNODE_INPUT_TOO_NEW; - strNotCapableReason = strprintf("%s - %d confirmations", GetStatus(), nInputAge); - LogPrintf("CActiveMasternode::ManageState -- %s\n", strNotCapableReason); - return; - } - - LOCK(pwalletMain->cs_wallet); - pwalletMain->LockCoin(vin.prevout); - - CMasternodeBroadcast mnb; - if(!CMasternodeBroadcast::Create(vin, service, keyCollateral, pubKeyCollateral, keyMasternode, pubKeyMasternode, strError, mnb)) { - strNotCapableReason = "Error on CMasternodeBroadcast::Create -- " + strError; - LogPrintf("CActiveMasternode::ManageState -- %s\n", strNotCapableReason); - return; - } - - //update to masternode list - LogPrintf("CActiveMasternode::ManageState -- Update Masternode List\n"); - mnodeman.UpdateMasternodeList(mnb); - - //send to all peers - LogPrintf("CActiveMasternode::ManageState -- Relay broadcast, vin=%s\n", vin.ToString()); - mnb.Relay(); - - LogPrintf("CActiveMasternode::ManageState -- Is capable master node!\n"); - nState = ACTIVE_MASTERNODE_STARTED; - - return; - } else { - strNotCapableReason = "Could not find suitable coins!"; - LogPrintf("CActiveMasternode::ManageState -- %s\n", strNotCapableReason); - return; - } + if(eType == MASTERNODE_UNKNOWN) { + ManageStateInitial(); } - //send to all peers - if(!SendMasternodePing(strError)) { - LogPrintf("CActiveMasternode::ManageState -- Error on SendMasternodePing(): %s\n", strError); + if(eType == MASTERNODE_REMOTE) { + ManageStateRemote(); + } + else { + ManageStateLocal(); + } + + if(fPingerEnabled) { + std::string strError; + if(!SendMasternodePing(strError)) { + LogPrintf("CActiveMasternode::ManageState -- Error on SendMasternodePing(): %s\n", strError); + } } } @@ -147,10 +64,29 @@ std::string CActiveMasternode::GetStatus() } } +std::string CActiveMasternode::GetType() +{ + std::string strType; + switch(eType) { + case MASTERNODE_UNKNOWN: + strType = "UNKNOWN"; + break; + case MASTERNODE_REMOTE: + strType = "REMOTE"; + break; + case MASTERNODE_LOCAL: + strType = "LOCAL"; + break; + default: + strType = "UNKNOWN"; + break; + } + return strType; +} + bool CActiveMasternode::SendMasternodePing(std::string& strErrorRet) { - if(nState != ACTIVE_MASTERNODE_STARTED) { - strErrorRet = "Masternode is not in a running status"; + if(vin == CTxIn()) { return false; } @@ -160,34 +96,25 @@ bool CActiveMasternode::SendMasternodePing(std::string& strErrorRet) return false; } - // Update lastPing for our masternode in Masternode list - CMasternode* pmn = mnodeman.Find(vin); - if(pmn != NULL) { - if(pmn->IsPingedWithin(MASTERNODE_MIN_MNP_SECONDS, mnp.sigTime)) { - strErrorRet = "Too early to send Masternode Ping"; - return false; - } - - pmn->lastPing = mnp; - mnodeman.mapSeenMasternodePing.insert(std::make_pair(mnp.GetHash(), mnp)); - - //mnodeman.mapSeenMasternodeBroadcast.lastPing is probably outdated, so we'll update it - CMasternodeBroadcast mnb(*pmn); - uint256 hash = mnb.GetHash(); - if(mnodeman.mapSeenMasternodeBroadcast.count(hash)) - mnodeman.mapSeenMasternodeBroadcast[hash].lastPing = mnp; - - LogPrintf("CActiveMasternode::SendMasternodePing -- Relaying ping, collateral=%s\n", vin.ToString()); - mnp.Relay(); - - return true; - } else { - // Seems like we are trying to send a ping while the Masternode is not registered in the network - strErrorRet = "PrivateSend Masternode List doesn't include our Masternode, shutting down Masternode pinging service! " + vin.ToString(); + if(!mnodeman.Has(vin)) { + strErrorRet = "Masternode List doesn't include our Masternode, shutting down Masternode pinging service! " + vin.ToString(); nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = strErrorRet; return false; } + + // Update lastPing for our masternode in Masternode list + if(mnodeman.IsMasternodePingedWithin(vin, MASTERNODE_MIN_MNP_SECONDS, mnp.sigTime)) { + strErrorRet = "Too early to send Masternode Ping"; + return false; + } + + mnodeman.SetMasternodeLastPing(vin, mnp); + + LogPrintf("CActiveMasternode::SendMasternodePing -- Relaying ping, collateral=%s\n", vin.ToString()); + mnp.Relay(); + + return true; } // when starting a Masternode, this can enable to run as a hot wallet with no funds @@ -205,3 +132,145 @@ bool CActiveMasternode::EnableRemoteMasterNode(CTxIn& vinNew, CService& serviceN return true; } + +void CActiveMasternode::ManageStateInitial() +{ + LogPrint("masternode", "CActiveMasternode::ManageStateInitial -- Start status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetType(), fPingerEnabled); + + // Check that our local network configuration is correct + if(!GetLocal(service)) { + strNotCapableReason = "Can't detect external address. Please consider using the externalip configuration option if problem persists."; + LogPrintf("CActiveMasternode::ManageStateInitial -- not capable: %s\n", strNotCapableReason); + return; + } + + int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + if(Params().NetworkIDString() == CBaseChainParams::MAIN) { + if(service.GetPort() != mainnetDefaultPort) { + strNotCapableReason = strprintf("Invalid port: %u - only %d is supported on mainnet.", service.GetPort(), mainnetDefaultPort); + LogPrintf("CActiveMasternode::ManageStateInitial -- not capable: %s\n", strNotCapableReason); + return; + } + } else if(service.GetPort() == mainnetDefaultPort) { + strNotCapableReason = strprintf("Invalid port: %u - %d is only supported on mainnet.", service.GetPort(), mainnetDefaultPort); + LogPrintf("CActiveMasternode::ManageStateInitial -- not capable: %s\n", strNotCapableReason); + return; + } + + LogPrintf("CActiveMasternode::ManageState -- Checking inbound connection to '%s'\n", service.ToString()); + + if(!ConnectNode((CAddress)service, NULL, true)) { + strNotCapableReason = "Could not connect to " + service.ToString(); + LogPrintf("CActiveMasternode::ManageStateInitial -- not capable: %s\n", strNotCapableReason); + return; + } + + // Default to REMOTE + eType = MASTERNODE_REMOTE; + + // Check if wallet funds are available + if(!pwalletMain) { + strNotCapableReason = "Wallet not available."; + LogPrintf("CActiveMasternode::ManageStateInitial -- not capable: %s\n", strNotCapableReason); + return; + } + + if(pwalletMain->IsLocked()) { + strNotCapableReason = "Wallet is locked."; + LogPrintf("CActiveMasternode::ManageStateInitial -- not capable: %s\n", strNotCapableReason); + return; + } + + if(pwalletMain->GetBalance() < 1000*COIN) { + strNotCapableReason = "Wallet balance is < 1000 DASH"; + LogPrintf("CActiveMasternode::ManageStateInitial -- not capable: %s\n", strNotCapableReason); + return; + } + + // Choose coins to use + CPubKey pubKeyCollateral; + CKey keyCollateral; + + // If collateral is found switch to LOCAL mode + if(pwalletMain->GetMasternodeVinAndKeys(vin, pubKeyCollateral, keyCollateral)) { + eType = MASTERNODE_LOCAL; + } + + LogPrint("masternode", "CActiveMasternode::ManageStateInitial -- End status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetType(), fPingerEnabled); +} + +void CActiveMasternode::ManageStateRemote() +{ + LogPrint("masternode", "CActiveMasternode::ManageStateRemote -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetType(), fPingerEnabled); + mnodeman.CheckMasternode(pubKeyMasternode); + masternode_info_t infoMn = mnodeman.GetMasternodeInfo(pubKeyMasternode); + if(infoMn.fInfoValid) { + if(infoMn.nProtocolVersion != PROTOCOL_VERSION) { + nState = ACTIVE_MASTERNODE_NOT_CAPABLE; + strNotCapableReason = "Invalid protocol version"; + return; + } + vin = infoMn.vin; + service = infoMn.addr; + fPingerEnabled = true; + if(((infoMn.nActiveState == CMasternode::MASTERNODE_ENABLED) || + (infoMn.nActiveState == CMasternode::MASTERNODE_PRE_ENABLED) || + (infoMn.nActiveState == CMasternode::MASTERNODE_WATCHDOG_EXPIRED))) { + nState = ACTIVE_MASTERNODE_STARTED; + } + else { + nState = ACTIVE_MASTERNODE_NOT_CAPABLE; + strNotCapableReason = "Masternode in EXPIRED state"; + } + } + else { + fPingerEnabled = false; + strNotCapableReason = "Masternode not in masternode list"; + nState = ACTIVE_MASTERNODE_NOT_CAPABLE; + } +} + +void CActiveMasternode::ManageStateLocal() +{ + LogPrint("masternode", "CActiveMasternode::ManageStateLocal -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetType(), fPingerEnabled); + if(nState == ACTIVE_MASTERNODE_STARTED) { + return; + } + + // Choose coins to use + CPubKey pubKeyCollateral; + CKey keyCollateral; + + if(pwalletMain->GetMasternodeVinAndKeys(vin, pubKeyCollateral, keyCollateral)) { + int nInputAge = GetInputAge(vin); + if(nInputAge < Params().GetConsensus().nMasternodeMinimumConfirmations){ + nState = ACTIVE_MASTERNODE_INPUT_TOO_NEW; + strNotCapableReason = strprintf("%s - %d confirmations", GetStatus(), nInputAge); + LogPrintf("CActiveMasternode::ManageStateLocal -- %s\n", strNotCapableReason); + return; + } + + { + LOCK(pwalletMain->cs_wallet); + pwalletMain->LockCoin(vin.prevout); + } + + CMasternodeBroadcast mnb; + std::string strError; + if(!CMasternodeBroadcast::Create(vin, service, keyCollateral, pubKeyCollateral, keyMasternode, pubKeyMasternode, strError, mnb)) { + strNotCapableReason = "Error on CMasternodeBroadcast::Create -- " + strError; + LogPrintf("CActiveMasternode::ManageStateLocal -- %s\n", strNotCapableReason); + return; + } + + //update to masternode list + LogPrintf("CActiveMasternode::ManageStateLocal -- Update Masternode List\n"); + mnodeman.UpdateMasternodeList(mnb); + + //send to all peers + LogPrintf("CActiveMasternode::ManageStateLocal -- Relay broadcast, vin=%s\n", vin.ToString()); + mnb.Relay(); + fPingerEnabled = true; + nState = ACTIVE_MASTERNODE_STARTED; + } +} diff --git a/src/activemasternode.h b/src/activemasternode.h index c01e0797c..a9dc25b07 100644 --- a/src/activemasternode.h +++ b/src/activemasternode.h @@ -22,10 +22,21 @@ extern CActiveMasternode activeMasternode; // Responsible for activating the Masternode and pinging the network class CActiveMasternode { +public: + enum masternode_type_enum_t { + MASTERNODE_UNKNOWN = 0, + MASTERNODE_REMOTE = 1, + MASTERNODE_LOCAL = 2 + }; + private: // critical section to protect the inner data structures mutable CCriticalSection cs; + masternode_type_enum_t eType; + + bool fPingerEnabled; + /// Ping Masternode bool SendMasternodePing(std::string& strErrorRet); @@ -41,15 +52,28 @@ public: int nState; // should be one of ACTIVE_MASTERNODE_XXXX std::string strNotCapableReason; - CActiveMasternode() : nState(ACTIVE_MASTERNODE_INITIAL) {} + CActiveMasternode() + : eType(MASTERNODE_UNKNOWN), + fPingerEnabled(false), + nState(ACTIVE_MASTERNODE_INITIAL) + {} /// Manage state of active Masternode void ManageState(); std::string GetStatus(); + std::string GetType(); + /// Enable cold wallet mode (run a Masternode with no funds) bool EnableRemoteMasterNode(CTxIn& vinNew, CService& serviceNew); + +private: + void ManageStateInitial(); + + void ManageStateRemote(); + + void ManageStateLocal(); }; #endif diff --git a/src/governance-classes.cpp b/src/governance-classes.cpp index 3689299c5..61ccb6b09 100644 --- a/src/governance-classes.cpp +++ b/src/governance-classes.cpp @@ -295,6 +295,7 @@ std::vector CGovernanceTriggerManager::GetActiveTriggers() bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) { + LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- Start nBlockHeight = %d\n", nBlockHeight); if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) { return false; } @@ -303,11 +304,14 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) // GET ALL ACTIVE TRIGGERS std::vector vecTriggers = triggerman.GetActiveTriggers(); + LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- vecTriggers.size() = %d\n", vecTriggers.size()); + DBG( cout << "IsSuperblockTriggered Number triggers = " << vecTriggers.size() << endl; ); BOOST_FOREACH(CSuperblock_sptr pSuperblock, vecTriggers) { if(!pSuperblock) { + LogPrintf("CSuperblockManager::IsSuperblockTriggered -- Non-superblock found, continuing\n"); DBG( cout << "IsSuperblockTriggered Not a superblock, continuing " << endl; ); continue; } @@ -315,27 +319,35 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) CGovernanceObject* pObj = pSuperblock->GetGovernanceObject(); if(!pObj) { + LogPrintf("CSuperblockManager::IsSuperblockTriggered -- pObj == NULL, continuing\n"); DBG( cout << "IsSuperblockTriggered pObj is NULL, continuing" << endl; ); continue; } + LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- data = %s\n", pObj->GetDataAsString()); + // note : 12.1 - is epoch calculation correct? if(nBlockHeight != pSuperblock->GetBlockStart()) { + LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- block height doesn't match nBlockHeight = %d, blockStart = %d, continuing\n", + nBlockHeight, + pSuperblock->GetBlockStart()); DBG( cout << "IsSuperblockTriggered Not the target block, continuing" - << ", nBlockHeight = " << nBlockHeight - << ", superblock->GetBlockStart() = " << pSuperblock->GetBlockStart() - << endl; ); + << ", nBlockHeight = " << nBlockHeight + << ", superblock->GetBlockStart() = " << pSuperblock->GetBlockStart() + << endl; ); continue; } // MAKE SURE THIS TRIGGER IS ACTIVE VIA FUNDING CACHE FLAG if(pObj->fCachedFunding) { + LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- fCacheFunding = true, returning true\n"); DBG( cout << "IsSuperblockTriggered returning true" << endl; ); return true; } else { + LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- fCacheFunding = false, continuing\n"); DBG( cout << "IsSuperblockTriggered No fCachedFunding, continuing" << endl; ); } } @@ -402,7 +414,7 @@ void CSuperblockManager::CreateSuperblock(CMutableTransaction& txNewRet, int nBl CSuperblock_sptr pSuperblock; if(!CSuperblockManager::GetBestSuperblock(pSuperblock, nBlockHeight)) { - LogPrint("superblock", "CSuperblockManager::CreateSuperblock -- Can't find superblock for height %d\n", nBlockHeight); + LogPrint("gobject", "CSuperblockManager::CreateSuperblock -- Can't find superblock for height %d\n", nBlockHeight); DBG( cout << "CSuperblockManager::CreateSuperblock Failed to get superblock for height, returning" << endl; ); return; } @@ -507,6 +519,9 @@ CSuperblock(uint256& nHash) std::string strAmounts = obj["payment_amounts"].get_str(); ParsePaymentSchedule(strAddresses, strAmounts); + LogPrint("gobject", "CSuperblock -- nEpochStart = %d, strAddresses = %s, strAmounts = %s, vecPayments.size() = %d\n", + nEpochStart, strAddresses, strAmounts, vecPayments.size()); + DBG( cout << "CSuperblock Constructor End" << endl; ); } @@ -554,13 +569,15 @@ void CSuperblock::ParsePaymentSchedule(std::string& strPaymentAddresses, std::st if (vecParsed1.size() != vecParsed2.size()) { std::ostringstream ostr; - ostr << "CSuperblock::ParsePaymentSchedule Mismatched payments and amounts"; + ostr << "CSuperblock::ParsePaymentSchedule -- Mismatched payments and amounts"; + LogPrintf("%s\n", ostr.str()); throw std::runtime_error(ostr.str()); } if (vecParsed1.size() == 0) { std::ostringstream ostr; - ostr << "CSuperblock::ParsePaymentSchedule Error no payments"; + ostr << "CSuperblock::ParsePaymentSchedule -- Error no payments"; + LogPrintf("%s\n", ostr.str()); throw std::runtime_error(ostr.str()); } @@ -576,7 +593,8 @@ void CSuperblock::ParsePaymentSchedule(std::string& strPaymentAddresses, std::st CBitcoinAddress address(vecParsed1[i]); if (!address.IsValid()) { std::ostringstream ostr; - ostr << "CSuperblock::ParsePaymentSchedule Invalid Dash Address : " << vecParsed1[i]; + ostr << "CSuperblock::ParsePaymentSchedule -- Invalid Dash Address : " << vecParsed1[i]; + LogPrintf("%s\n", ostr.str()); throw std::runtime_error(ostr.str()); } @@ -595,6 +613,14 @@ void CSuperblock::ParsePaymentSchedule(std::string& strPaymentAddresses, std::st if(payment.IsValid()) { vecPayments.push_back(payment); } + else { + vecPayments.clear(); + std::ostringstream ostr; + ostr << "CSuperblock::ParsePaymentSchedule -- Invalid payment found: address = " << address.ToString() + << ", amount = " << nAmount; + LogPrintf("%s\n", ostr.str()); + throw std::runtime_error(ostr.str()); + } } } @@ -646,6 +672,9 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b int nPayments = CountPayments(); int nMinerPayments = nOutputs - nPayments; + LogPrint("gobject", "CSuperblock::IsValid nOutputs = %d, nPayments = %d, strData = %s\n", + nOutputs, nPayments, GetGovernanceObject()->strData); + // We require an exact match (including order) between the expected // superblock payments and the payments actually in the block, after // skipping any initial miner payments. @@ -716,7 +745,7 @@ std::string CSuperblockManager::GetRequiredPaymentsString(int nBlockHeight) CSuperblock_sptr pSuperblock; if(!GetBestSuperblock(pSuperblock, nBlockHeight)) { - LogPrint("superblock", "CSuperblockManager::GetRequiredPaymentsString -- Can't find superblock for height %d\n", nBlockHeight); + LogPrint("gobject", "CSuperblockManager::GetRequiredPaymentsString -- Can't find superblock for height %d\n", nBlockHeight); return "error"; } diff --git a/src/governance-classes.h b/src/governance-classes.h index a34cc8fb3..04682d955 100644 --- a/src/governance-classes.h +++ b/src/governance-classes.h @@ -99,27 +99,33 @@ public: CAmount nAmount; CGovernancePayment() - { - SetNull(); - } + :fValid(false), + script(), + nAmount(0) + {} CGovernancePayment(CBitcoinAddress addrIn, CAmount nAmountIn) + :fValid(false), + script(), + nAmount(0) { try { CTxDestination dest = addrIn.Get(); script = GetScriptForDestination(dest); nAmount = nAmountIn; - } catch(...) { - SetNull(); //set fValid to false + fValid = true; + } + catch(std::exception& e) + { + LogPrintf("CGovernancePayment Payment not valid: addrIn = %s, nAmountIn = %d, what = %s\n", + addrIn.ToString(), nAmountIn, e.what()); + } + catch(...) + { + LogPrintf("CGovernancePayment Payment not valid: addrIn = %s, nAmountIn = %d\n", + addrIn.ToString(), nAmountIn); } - } - - void SetNull() - { - script = CScript(); - nAmount = 0; - fValid = false; } bool IsValid() { return fValid; } diff --git a/src/governance-vote.cpp b/src/governance-vote.cpp index 687356194..a196a13cc 100644 --- a/src/governance-vote.cpp +++ b/src/governance-vote.cpp @@ -283,9 +283,8 @@ bool CGovernanceVote::IsValid(bool fSignatureCheck) return false; } - CMasternode* pmn = mnodeman.Find(vinMasternode); - if(pmn == NULL) - { + masternode_info_t infoMn = mnodeman.GetMasternodeInfo(vinMasternode); + if(!infoMn.fInfoValid) { LogPrint("gobject", "CGovernanceVote::IsValid -- Unknown Masternode - %s\n", vinMasternode.prevout.ToStringShort()); return false; } @@ -296,7 +295,7 @@ bool CGovernanceVote::IsValid(bool fSignatureCheck) std::string strMessage = vinMasternode.prevout.ToStringShort() + "|" + nParentHash.ToString() + "|" + boost::lexical_cast(nVoteSignal) + "|" + boost::lexical_cast(nVoteOutcome) + "|" + boost::lexical_cast(nTime); - if(!darkSendSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, strError)) { + if(!darkSendSigner.VerifyMessage(infoMn.pubKeyMasternode, vchSig, strMessage, strError)) { LogPrintf("CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError); return false; } diff --git a/src/governance-vote.h b/src/governance-vote.h index 57aba3593..5daaced27 100644 --- a/src/governance-vote.h +++ b/src/governance-vote.h @@ -109,9 +109,9 @@ public: int64_t GetTimestamp() const { return nTime; } - vote_signal_enum_t GetSignal() { return vote_signal_enum_t(nVoteSignal); } + vote_signal_enum_t GetSignal() const { return vote_signal_enum_t(nVoteSignal); } - vote_outcome_enum_t GetOutcome() { return vote_outcome_enum_t(nVoteOutcome); } + vote_outcome_enum_t GetOutcome() const { return vote_outcome_enum_t(nVoteOutcome); } const uint256& GetParentHash() const { return nParentHash; } @@ -129,6 +129,8 @@ public: CTxIn& GetVinMasternode() { return vinMasternode; } + const CTxIn& GetVinMasternode() const { return vinMasternode; } + /** * GetHash() * @@ -148,8 +150,12 @@ public: std::string ToString() { - std::string strRet = CGovernanceVoting::ConvertOutcomeToString(GetOutcome()) + ":" + CGovernanceVoting::ConvertSignalToString(GetSignal()); - return strRet; + std::ostringstream ostr; + ostr << vinMasternode.ToString() << ":" + << nTime << ":" + << CGovernanceVoting::ConvertOutcomeToString(GetOutcome()) << ":" + << CGovernanceVoting::ConvertSignalToString(GetSignal()); + return ostr.str(); } /** diff --git a/src/governance.cpp b/src/governance.cpp index 997a06653..f2ec9ce87 100644 --- a/src/governance.cpp +++ b/src/governance.cpp @@ -36,6 +36,8 @@ CGovernanceManager::CGovernanceManager() mapSeenGovernanceObjects(), mapSeenVotes(), mapOrphanVotes(), + mapVotesByHash(), + mapVotesByType(), mapLastMasternodeTrigger(), cs() {} @@ -186,8 +188,7 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, std::string& strCommand, C // FIND THE MASTERNODE OF THE VOTER - CMasternode* pmn = mnodeman.Find(vote.GetVinMasternode()); - if(pmn == NULL) { + if(!mnodeman.Has(vote.GetVinMasternode())) { LogPrint("gobject", "gobject - unknown masternode - vin: %s\n", vote.GetVinMasternode().ToString()); mnodeman.AskForMN(pfrom, vote.GetVinMasternode()); return; @@ -212,7 +213,7 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, std::string& strCommand, C if(AddOrUpdateVote(vote, pfrom, strError)) { vote.Relay(); masternodeSync.AddedBudgetItem(vote.GetHash()); - pmn->AddGovernanceVote(vote.GetParentHash()); + mnodeman.AddGovernanceVote(vote.GetVinMasternode(), vote.GetParentHash()); } LogPrint("gobject", "NEW governance vote: %s\n", vote.GetHash().ToString()); @@ -267,11 +268,15 @@ bool CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj) << ", nObjectType = " << govobj.nObjectType << endl; ); - if(govobj.nObjectType == GOVERNANCE_OBJECT_TRIGGER) { + switch(govobj.nObjectType) { + case GOVERNANCE_OBJECT_TRIGGER: mapLastMasternodeTrigger[govobj.vinMasternode.prevout] = nCachedBlockHeight; DBG( cout << "CGovernanceManager::AddGovernanceObject Before AddNewTrigger" << endl; ); triggerman.AddNewTrigger(govobj.GetHash()); DBG( cout << "CGovernanceManager::AddGovernanceObject After AddNewTrigger" << endl; ); + break; + default: + break; } DBG( cout << "CGovernanceManager::AddGovernanceObject END" << endl; ); @@ -283,8 +288,18 @@ void CGovernanceManager::UpdateCachesAndClean() { LogPrintf("CGovernanceManager::UpdateCachesAndClean \n"); + std::vector vecDirtyHashes = mnodeman.GetAndClearDirtyGovernanceObjectHashes(); + LOCK(cs); + for(size_t i = 0; i < vecDirtyHashes.size(); ++i) { + object_m_it it = mapObjects.find(vecDirtyHashes[i]); + if(it == mapObjects.end()) { + continue; + } + it->second.fDirtyCache = true; + } + // DOUBLE CHECK THAT WE HAVE A VALID POINTER TO TIP if(!pCurrentBlockIndex) return; @@ -322,6 +337,7 @@ void CGovernanceManager::UpdateCachesAndClean() if(pObj->fCachedDelete || pObj->fExpired) { LogPrintf("UpdateCachesAndClean --- erase obj %s\n", (*it).first.ToString()); + mnodeman.RemoveGovernanceObject(pObj->GetHash()); mapObjects.erase(it++); } else { ++it; @@ -561,6 +577,9 @@ bool CGovernanceManager::AddOrUpdateVote(const CGovernanceVote& vote, CNode* pfr { pGovObj->fDirtyCache = true; UpdateCachesAndClean(); + if(pGovObj->GetObjectType() == GOVERNANCE_OBJECT_WATCHDOG) { + mnodeman.UpdateWatchdogVoteTime(vote.GetVinMasternode()); + } } else { LogPrintf("Governance object not found! Can't update fDirtyCache - %s\n", vote.GetParentHash().ToString()); } @@ -568,15 +587,27 @@ bool CGovernanceManager::AddOrUpdateVote(const CGovernanceVote& vote, CNode* pfr return true; } -bool CGovernanceManager::MasternodeRateCheck(const CTxIn& vin) +bool CGovernanceManager::MasternodeRateCheck(const CTxIn& vin, int nObjectType) { LOCK(cs); + + int mindiff = 0; + switch(nObjectType) { + case GOVERNANCE_OBJECT_TRIGGER: + mindiff = Params().GetConsensus().nSuperblockCycle - Params().GetConsensus().nSuperblockCycle / 10; + break; + case GOVERNANCE_OBJECT_WATCHDOG: + mindiff = 1; + break; + default: + break; + } + txout_m_it it = mapLastMasternodeTrigger.find(vin.prevout); if(it == mapLastMasternodeTrigger.end()) { return true; } // Allow 1 trigger per mn per cycle, with a small fudge factor - int mindiff = Params().GetConsensus().nSuperblockCycle - Params().GetConsensus().nSuperblockCycle / 10; if((nCachedBlockHeight - it->second) > mindiff) { return true; } @@ -587,79 +618,73 @@ bool CGovernanceManager::MasternodeRateCheck(const CTxIn& vin) } CGovernanceObject::CGovernanceObject() + : cs(), + nHashParent(), + nRevision(0), + nTime(0), + nCollateralHash(), + strData(), + nObjectType(GOVERNANCE_OBJECT_UNKNOWN), + vinMasternode(), + vchSig(), + fCachedLocalValidity(false), + strLocalValidityError(), + fCachedFunding(false), + fCachedValid(true), + fCachedDelete(false), + fCachedEndorsed(false), + fDirtyCache(true), + fUnparsable(false), + fExpired(false) { - // MAIN OBJECT DATA - - nTime = 0; - nObjectType = GOVERNANCE_OBJECT_UNKNOWN; - - nHashParent = uint256(); //parent object, 0 is root - nRevision = 0; //object revision in the system - nCollateralHash = uint256(); //fee-tx - - // CACHING FOR VARIOUS FLAGS - - fCachedFunding = false; - fCachedValid = true; - fCachedDelete = false; - fCachedEndorsed = false; - fDirtyCache = true; - fUnparsable = false; - fExpired = false; - // PARSE JSON DATA STORAGE (STRDATA) LoadData(); } CGovernanceObject::CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTimeIn, uint256 nCollateralHashIn, std::string strDataIn) + : cs(), + nHashParent(nHashParentIn), + nRevision(nRevisionIn), + nTime(nTimeIn), + nCollateralHash(nCollateralHashIn), + strData(strDataIn), + nObjectType(GOVERNANCE_OBJECT_UNKNOWN), + vinMasternode(), + vchSig(), + fCachedLocalValidity(false), + strLocalValidityError(), + fCachedFunding(false), + fCachedValid(true), + fCachedDelete(false), + fCachedEndorsed(false), + fDirtyCache(true), + fUnparsable(false), + fExpired(false) { - // MAIN OBJECT DATA - - nHashParent = nHashParentIn; //parent object, 0 is root - nRevision = nRevisionIn; //object revision in the system - nTime = nTimeIn; - nCollateralHash = nCollateralHashIn; //fee-tx - nObjectType = GOVERNANCE_OBJECT_UNKNOWN; // Avoid having an uninitialized variable - strData = strDataIn; - - // CACHING FOR VARIOUS FLAGS - - fCachedFunding = false; - fCachedValid = true; - fCachedDelete = false; - fCachedEndorsed = false; - fDirtyCache = true; - fUnparsable = false; - fExpired = false; - // PARSE JSON DATA STORAGE (STRDATA) LoadData(); } CGovernanceObject::CGovernanceObject(const CGovernanceObject& other) -{ - // COPY OTHER OBJECT'S DATA INTO THIS OBJECT - - nHashParent = other.nHashParent; - nRevision = other.nRevision; - nTime = other.nTime; - nCollateralHash = other.nCollateralHash; - strData = other.strData; - nObjectType = other.nObjectType; - - fUnparsable = true; - - vinMasternode = other.vinMasternode; - vchSig = other.vchSig; - - // caching - fCachedFunding = other.fCachedFunding; - fCachedValid = other.fCachedValid; - fCachedDelete = other.fCachedDelete; - fCachedEndorsed = other.fCachedEndorsed; - fDirtyCache = other.fDirtyCache; - fExpired = other.fExpired; -} + : cs(), + nHashParent(other.nHashParent), + nRevision(other.nRevision), + nTime(other.nTime), + nCollateralHash(other.nCollateralHash), + strData(other.strData), + nObjectType(other.nObjectType), + vinMasternode(other.vinMasternode), + vchSig(other.vchSig), + fCachedLocalValidity(other.fCachedLocalValidity), + strLocalValidityError(other.strLocalValidityError), + fCachedFunding(other.fCachedFunding), + fCachedValid(other.fCachedValid), + fCachedDelete(other.fCachedDelete), + fCachedEndorsed(other.fCachedEndorsed), + fDirtyCache(other.fDirtyCache), + fUnparsable(other.fUnparsable), + fExpired(other.fExpired) +{} void CGovernanceObject::SetMasternodeInfo(const CTxIn& vin) { @@ -789,6 +814,7 @@ void CGovernanceObject::LoadData() nObjectType = obj["type"].get_int(); } catch(std::exception& e) { + fUnparsable = true; std::ostringstream ostr; ostr << "CGovernanceObject::LoadData Error parsing JSON" << ", e.what() = " << e.what(); @@ -797,6 +823,7 @@ void CGovernanceObject::LoadData() return; } catch(...) { + fUnparsable = true; std::ostringstream ostr; ostr << "CGovernanceObject::LoadData Unknown Error parsing JSON"; DBG( cout << ostr.str() << endl; ); @@ -854,9 +881,14 @@ bool CGovernanceObject::IsValidLocally(const CBlockIndex* pindex, std::string& s return true; } + if(fUnparsable) { + return false; + } + switch(nObjectType) { case GOVERNANCE_OBJECT_PROPOSAL: case GOVERNANCE_OBJECT_TRIGGER: + case GOVERNANCE_OBJECT_WATCHDOG: break; default: strError = strprintf("Invalid object type %d", nObjectType); @@ -872,29 +904,25 @@ bool CGovernanceObject::IsValidLocally(const CBlockIndex* pindex, std::string& s // CHECK COLLATERAL IF REQUIRED (HIGH CPU USAGE) - if(fCheckCollateral) { - if(nObjectType == GOVERNANCE_OBJECT_TRIGGER) { + if(fCheckCollateral) { + if((nObjectType == GOVERNANCE_OBJECT_TRIGGER) || (nObjectType == GOVERNANCE_OBJECT_WATCHDOG)) { std::string strVin = vinMasternode.prevout.ToStringShort(); - CMasternode mn; - if(!mnodeman.Get(vinMasternode, mn)) { + masternode_info_t infoMn = mnodeman.GetMasternodeInfo(vinMasternode); + if(!infoMn.fInfoValid) { strError = "Masternode not found vin: " + strVin; return false; } - if(!mn.IsEnabled()) { - strError = "Masternode not enabled vin: " + strVin; - return false; - } // Check that we have a valid MN signature - if(!CheckSignature(mn.pubKeyMasternode)) { - strError = "Invalid masternode signature for: " + strVin + ", pubkey id = " + mn.pubKeyMasternode.GetID().ToString(); + if(!CheckSignature(infoMn.pubKeyMasternode)) { + strError = "Invalid masternode signature for: " + strVin + ", pubkey id = " + infoMn.pubKeyMasternode.GetID().ToString(); return false; } // Only perform rate check if we are synced because during syncing it is expected // that objects will be seen in rapid succession if(masternodeSync.IsSynced()) { - if(!governance.MasternodeRateCheck(vinMasternode)) { + if(!governance.MasternodeRateCheck(vinMasternode, nObjectType)) { strError = "Masternode attempting to create too many objects vin: " + strVin; return false; } @@ -932,6 +960,7 @@ CAmount CGovernanceObject::GetMinCollateralFee() switch(nObjectType) { case GOVERNANCE_OBJECT_PROPOSAL: return GOVERNANCE_PROPOSAL_FEE_TX; case GOVERNANCE_OBJECT_TRIGGER: return 0; + case GOVERNANCE_OBJECT_WATCHDOG: return 0; default: return MAX_MONEY; } } diff --git a/src/governance.h b/src/governance.h index c8edc36da..5dda79184 100644 --- a/src/governance.h +++ b/src/governance.h @@ -35,6 +35,7 @@ static const int MIN_GOVERNANCE_PEER_PROTO_VERSION = 70202; static const int GOVERNANCE_OBJECT_UNKNOWN = 0; static const int GOVERNANCE_OBJECT_PROPOSAL = 1; static const int GOVERNANCE_OBJECT_TRIGGER = 2; +static const int GOVERNANCE_OBJECT_WATCHDOG = 3; static const CAmount GOVERNANCE_PROPOSAL_FEE_TX = (0.33*COIN); @@ -205,7 +206,7 @@ public: void AddSeenVote(uint256 nHash, int status); - bool MasternodeRateCheck(const CTxIn& vin); + bool MasternodeRateCheck(const CTxIn& vin, int nObjectType); }; diff --git a/src/masternode-sync.cpp b/src/masternode-sync.cpp index ebf1503b0..442b78107 100644 --- a/src/masternode-sync.cpp +++ b/src/masternode-sync.cpp @@ -169,7 +169,9 @@ void CMasternodeSync::ProcessTick() if(!pCurrentBlockIndex) return; //the actual count of masternodes we have currently - int nMnCount = mnodeman.CountEnabled(); + int nMnCount = mnodeman.CountMasternodes(); + + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nMnCount = %d\n", nTick, nMnCount); // RESET SYNCING INCASE OF FAILURE { @@ -222,7 +224,7 @@ void CMasternodeSync::ProcessTick() } else if(nRequestedMasternodeAttempt < 4) { mnodeman.DsegUpdate(pnode); } else if(nRequestedMasternodeAttempt < 6) { - int nMnCount = mnodeman.CountEnabled(); + int nMnCount = mnodeman.CountMasternodes(); pnode->PushMessage(NetMsgType::MASTERNODEPAYMENTSYNC, nMnCount); //sync payment votes uint256 n = uint256(); pnode->PushMessage(NetMsgType::MNGOVERNANCESYNC, n); //sync masternode votes @@ -274,6 +276,8 @@ void CMasternodeSync::ProcessTick() /* Note: Is this activing up? It's probably related to int CMasternodeMan::GetEstimatedMasternodes(int nBlock) Surely doesn't work right for testnet currently */ // try to fetch data from at least two peers though + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nMnCount %d, Estimated masternode count required: %d\n", + nTick, nMnCount, mnodeman.GetEstimatedMasternodes(pCurrentBlockIndex->nHeight)*0.9); if(nRequestedMasternodeAttempt > 1 && nMnCount > mnodeman.GetEstimatedMasternodes(pCurrentBlockIndex->nHeight)*0.9) { LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- found enough data\n", nTick, nRequestedMasternodeAssets); SwitchToNextAsset(); diff --git a/src/masternode.cpp b/src/masternode.cpp index da2cebe82..16c9c644b 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -27,6 +27,7 @@ CMasternode::CMasternode() : nLastDsq(0), nTimeLastChecked(0), nTimeLastPaid(0), + nTimeLastWatchdogVote(0), nActiveState(MASTERNODE_ENABLED), nCacheCollateralBlock(0), nBlockLastPaid(0), @@ -46,6 +47,7 @@ CMasternode::CMasternode(CService addrNew, CTxIn vinNew, CPubKey pubKeyCollatera nLastDsq(0), nTimeLastChecked(0), nTimeLastPaid(0), + nTimeLastWatchdogVote(0), nActiveState(MASTERNODE_ENABLED), nCacheCollateralBlock(0), nBlockLastPaid(0), @@ -65,6 +67,7 @@ CMasternode::CMasternode(const CMasternode& other) : nLastDsq(other.nLastDsq), nTimeLastChecked(other.nTimeLastChecked), nTimeLastPaid(other.nTimeLastPaid), + nTimeLastWatchdogVote(other.nTimeLastWatchdogVote), nActiveState(other.nActiveState), nCacheCollateralBlock(other.nCacheCollateralBlock), nBlockLastPaid(other.nBlockLastPaid), @@ -84,6 +87,7 @@ CMasternode::CMasternode(const CMasternodeBroadcast& mnb) : nLastDsq(mnb.nLastDsq), nTimeLastChecked(0), nTimeLastPaid(0), + nTimeLastWatchdogVote(0), nActiveState(MASTERNODE_ENABLED), nCacheCollateralBlock(0), nBlockLastPaid(0), @@ -137,6 +141,13 @@ arith_uint256 CMasternode::CalculateScore(const uint256& blockHash) void CMasternode::Check(bool fForce) { + LOCK(cs); + + bool fWatchdogActive = mnodeman.IsWatchdogActive(); + + LogPrint("masternode", "CMasternode::Check start -- vin = %s\n", + vin.prevout.ToStringShort()); + //once spent, stop doing the checks if(nActiveState == MASTERNODE_OUTPOINT_SPENT) return; @@ -145,12 +156,15 @@ void CMasternode::Check(bool fForce) if(!fForce && (GetTime() - nTimeLastChecked < MASTERNODE_CHECK_SECONDS)) return; nTimeLastChecked = GetTime(); - bool fRemove = // If there were no pings for quite a long time ... - !IsPingedWithin(MASTERNODE_REMOVAL_SECONDS) || - // or masternode doesn't meet payment protocol requirements ... - 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((nActiveState == MASTERNODE_WATCHDOG_EXPIRED) && !fWatchdogActive) { + // Redo the checks + nActiveState = MASTERNODE_ENABLED; + } + + // 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 @@ -162,7 +176,9 @@ void CMasternode::Check(bool fForce) } if(!IsPingedWithin(MASTERNODE_EXPIRATION_SECONDS)) { - nActiveState = MASTERNODE_EXPIRED; + if(nActiveState != MASTERNODE_WATCHDOG_EXPIRED) { + nActiveState = MASTERNODE_EXPIRED; + } // RESCAN AFFECTED VOTES FlagGovernanceItemsAsDirty(); @@ -170,7 +186,10 @@ void CMasternode::Check(bool fForce) } if(lastPing.sigTime - sigTime < MASTERNODE_MIN_MNP_SECONDS) { - nActiveState = MASTERNODE_PRE_ENABLED; + if(nActiveState != MASTERNODE_WATCHDOG_EXPIRED) { + nActiveState = MASTERNODE_PRE_ENABLED; + } + return; } @@ -192,6 +211,14 @@ void CMasternode::Check(bool fForce) } } + 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; + } + nActiveState = MASTERNODE_ENABLED; // OK } @@ -203,6 +230,24 @@ bool CMasternode::IsValidNetAddr() (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::GetStatus() { switch(nActiveState) { @@ -211,6 +256,7 @@ std::string CMasternode::GetStatus() 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"; default: return "UNKNOWN"; } } @@ -458,7 +504,7 @@ bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDos) if(pmn != NULL) { // nothing to do here if we already know about this masternode and it's (pre)enabled - if(pmn->IsEnabled() || pmn->IsPreEnabled()) return true; + 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); } @@ -722,7 +768,7 @@ bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled, bool fChec return false; } - if (fRequireEnabled && !pmn->IsEnabled() && !pmn->IsPreEnabled()) 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 @@ -780,27 +826,45 @@ void CMasternodePing::Relay() void CMasternode::AddGovernanceVote(uint256 nGovernanceObjectHash) { - if(mapGovernaceObjectsVotedOn.count(nGovernanceObjectHash)) { - mapGovernaceObjectsVotedOn[nGovernanceObjectHash]++; + if(mapGovernanceObjectsVotedOn.count(nGovernanceObjectHash)) { + mapGovernanceObjectsVotedOn[nGovernanceObjectHash]++; } else { - mapGovernaceObjectsVotedOn.insert(std::make_pair(nGovernanceObjectHash, 1)); + mapGovernanceObjectsVotedOn.insert(std::make_pair(nGovernanceObjectHash, 1)); } } +void CMasternode::RemoveGovernanceObject(uint256 nGovernanceObjectHash) +{ + std::map::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::iterator it = mapGovernaceObjectsVotedOn.begin(); - while(it != mapGovernaceObjectsVotedOn.end()){ - CGovernanceObject *pObj = governance.FindGovernanceObject((*it).first); - - if(pObj) pObj->fDirtyCache = true; - ++it; + std::vector vecDirty; + { + std::map::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]); } } diff --git a/src/masternode.h b/src/masternode.h index acbc9d987..64e508c84 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -20,6 +20,7 @@ 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; // // The Masternode Ping Class : Contains a different serialize method for sending pings from masternodes throughout the network @@ -95,6 +96,36 @@ public: }; +struct masternode_info_t { + + masternode_info_t() + : vin(), + addr(), + pubKeyCollateralAddress(), + pubKeyMasternode(), + sigTime(0), + nLastDsq(0), + nTimeLastChecked(0), + nTimeLastPaid(0), + nTimeLastWatchdogVote(0), + nActiveState(0), + nProtocolVersion(0), + fInfoValid(false) + {} + + CTxIn vin; + CService addr; + CPubKey pubKeyCollateralAddress; + CPubKey pubKeyMasternode; + int64_t sigTime; //mnb message time + int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node + int64_t nTimeLastChecked; + int64_t nTimeLastPaid; + int64_t nTimeLastWatchdogVote; + int nActiveState; + int nProtocolVersion; + bool fInfoValid; +}; // // The Masternode Class. For managing the Darksend process. It contains the input of the 1000DRK, signature to prove @@ -112,7 +143,8 @@ public: MASTERNODE_ENABLED, MASTERNODE_EXPIRED, MASTERNODE_OUTPOINT_SPENT, - MASTERNODE_REMOVE + MASTERNODE_REMOVE, + MASTERNODE_WATCHDOG_EXPIRED }; CTxIn vin; @@ -125,6 +157,7 @@ public: int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node int64_t nTimeLastChecked; int64_t nTimeLastPaid; + int64_t nTimeLastWatchdogVote; int nActiveState; int nCacheCollateralBlock; int nBlockLastPaid; @@ -133,7 +166,7 @@ public: bool fUnitTest; // KEEP TRACK OF GOVERNANCE ITEMS EACH MASTERNODE HAS VOTE UPON FOR RECALCULATION - std::map mapGovernaceObjectsVotedOn; + std::map mapGovernanceObjectsVotedOn; CMasternode(); CMasternode(const CMasternode& other); @@ -155,13 +188,14 @@ public: READWRITE(nLastDsq); READWRITE(nTimeLastChecked); READWRITE(nTimeLastPaid); + READWRITE(nTimeLastWatchdogVote); READWRITE(nActiveState); READWRITE(nCacheCollateralBlock); READWRITE(nBlockLastPaid); READWRITE(nProtocolVersion); READWRITE(fAllowMixingTx); READWRITE(fUnitTest); - READWRITE(mapGovernaceObjectsVotedOn); + READWRITE(mapGovernanceObjectsVotedOn); } void swap(CMasternode& first, CMasternode& second) // nothrow @@ -181,13 +215,14 @@ public: swap(first.nLastDsq, second.nLastDsq); swap(first.nTimeLastChecked, second.nTimeLastChecked); swap(first.nTimeLastPaid, second.nTimeLastPaid); + swap(first.nTimeLastWatchdogVote, second.nTimeLastWatchdogVote); swap(first.nActiveState, second.nActiveState); swap(first.nCacheCollateralBlock, second.nCacheCollateralBlock); swap(first.nBlockLastPaid, second.nBlockLastPaid); swap(first.nProtocolVersion, second.nProtocolVersion); swap(first.fAllowMixingTx, second.fAllowMixingTx); swap(first.fUnitTest, second.fUnitTest); - swap(first.mapGovernaceObjectsVotedOn, second.mapGovernaceObjectsVotedOn); + swap(first.mapGovernanceObjectsVotedOn, second.mapGovernanceObjectsVotedOn); } // CALCULATE A RANK AGAINST OF GIVEN BLOCK @@ -212,8 +247,12 @@ public: bool IsEnabled() { return nActiveState == MASTERNODE_ENABLED; } bool IsPreEnabled() { return nActiveState == MASTERNODE_PRE_ENABLED; } + bool IsWatchdogExpired() { return nActiveState == MASTERNODE_WATCHDOG_EXPIRED; } + bool IsValidNetAddr(); + masternode_info_t GetInfo(); + std::string GetStatus(); int GetCollateralAge(); @@ -226,8 +265,10 @@ public: void AddGovernanceVote(uint256 nGovernanceObjectHash); // RECALCULATE CACHED STATUS FLAGS FOR ALL AFFECTED OBJECTS void FlagGovernanceItemsAsDirty(); - // TODO: There probably should be some method to clean mapGovernaceObjectsVotedOn map - // under some conditions. We shouldn't store everything in memory forever. + + void RemoveGovernanceObject(uint256 nGovernanceObjectHash); + + void UpdateWatchdogVoteTime(); CMasternode& operator=(CMasternode from) { diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 696603641..58c878916 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -35,8 +35,11 @@ struct CompareScoreMN } }; +const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-1"; + CMasternodeMan::CMasternodeMan() { nDsqCount = 0; + nLastWatchdogVoteTime = 0; } bool CMasternodeMan::Add(CMasternode &mn) @@ -78,6 +81,8 @@ void CMasternodeMan::Check() { LOCK(cs); + LogPrint("masternode", "CMasternodeMan::Check nLastWatchdogVoteTime = %d, IsWatchdogActive() = %d\n", nLastWatchdogVoteTime, IsWatchdogActive()); + BOOST_FOREACH(CMasternode& mn, vMasternodes) { mn.Check(); } @@ -180,10 +185,26 @@ void CMasternodeMan::Clear() mapSeenMasternodeBroadcast.clear(); mapSeenMasternodePing.clear(); nDsqCount = 0; + nLastWatchdogVoteTime = 0; +} + +int CMasternodeMan::CountMasternodes(int protocolVersion) +{ + LOCK(cs); + int i = 0; + protocolVersion = protocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : protocolVersion; + + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + if(mn.nProtocolVersion < protocolVersion) continue; + i++; + } + + return i; } int CMasternodeMan::CountEnabled(int protocolVersion) { + LOCK(cs); int i = 0; protocolVersion = protocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : protocolVersion; @@ -198,6 +219,7 @@ int CMasternodeMan::CountEnabled(int protocolVersion) int CMasternodeMan::CountByIP(int nNetworkType) { + LOCK(cs); int nNodeCount = 0; BOOST_FOREACH(CMasternode& mn, vMasternodes) @@ -292,6 +314,37 @@ bool CMasternodeMan::Get(const CTxIn& vin, CMasternode& masternode) return true; } +masternode_info_t CMasternodeMan::GetMasternodeInfo(const CTxIn& vin) +{ + masternode_info_t info; + LOCK(cs); + CMasternode* pMN = Find(vin); + if(!pMN) { + return info; + } + info = pMN->GetInfo(); + return info; +} + +masternode_info_t CMasternodeMan::GetMasternodeInfo(const CPubKey& pubKeyMasternode) +{ + masternode_info_t info; + LOCK(cs); + CMasternode* pMN = Find(pubKeyMasternode); + if(!pMN) { + return info; + } + info = pMN->GetInfo(); + return info; +} + +bool CMasternodeMan::Has(const CTxIn& vin) +{ + LOCK(cs); + CMasternode* pMN = Find(vin); + return (pMN != NULL); +} + // // Deterministically select the oldest/best masternode to pay on the network // @@ -544,7 +597,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData if(fLiteMode) return; //disable all Darksend/Masternode related functionality if(!masternodeSync.IsBlockchainSynced()) return; - LOCK(cs_process_message); + LOCK(cs); if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast CMasternodeBroadcast mnb; @@ -707,6 +760,7 @@ int CMasternodeMan::GetEstimatedMasternodes(int nBlock) } void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast mnb) { + LOCK(cs); mapSeenMasternodePing.insert(make_pair(mnb.lastPing.GetHash(), mnb.lastPing)); mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb)); @@ -725,6 +779,7 @@ void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast mnb) { } bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, int& nDos) { + LOCK(cs); nDos = 0; LogPrint("masternode", "CMasternodeMan::CheckMnbAndUpdateMasternodeList - Masternode broadcast, vin: %s\n", mnb.vin.ToString()); @@ -759,6 +814,7 @@ bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, i } void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex) { + LOCK(cs); if(fLiteMode) return; static bool IsFirstRun = true; @@ -776,3 +832,106 @@ void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex) { // every time is like the first time if winners list is not synced IsFirstRun = !masternodeSync.IsWinnersListSynced(); } + +void CMasternodeMan::UpdateWatchdogVoteTime(const CTxIn& vin) +{ + LOCK(cs); + CMasternode* pMN = Find(vin); + if(!pMN) { + return; + } + pMN->UpdateWatchdogVoteTime(); + nLastWatchdogVoteTime = GetTime(); +} + +bool CMasternodeMan::IsWatchdogActive() +{ + LOCK(cs); + // Check if any masternodes have voted recently, otherwise return false + return (GetTime() - nLastWatchdogVoteTime) <= MASTERNODE_WATCHDOG_MAX_SECONDS; +} + +void CMasternodeMan::AddGovernanceVote(const CTxIn& vin, uint256 nGovernanceObjectHash) +{ + LOCK(cs); + CMasternode* pMN = Find(vin); + if(!pMN) { + return; + } + pMN->AddGovernanceVote(nGovernanceObjectHash); +} + +void CMasternodeMan::RemoveGovernanceObject(uint256 nGovernanceObjectHash) +{ + LOCK(cs); + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + mn.RemoveGovernanceObject(nGovernanceObjectHash); + } +} + +void CMasternodeMan::CheckMasternode(const CTxIn& vin, bool fForce) +{ + LOCK(cs); + CMasternode* pMN = Find(vin); + if(!pMN) { + return; + } + pMN->Check(fForce); +} + +void CMasternodeMan::CheckMasternode(const CPubKey& pubKeyMasternode, bool fForce) +{ + LOCK(cs); + CMasternode* pMN = Find(pubKeyMasternode); + if(!pMN) { + return; + } + pMN->Check(fForce); +} + +int CMasternodeMan::GetMasternodeState(const CTxIn& vin) +{ + LOCK(cs); + CMasternode* pMN = Find(vin); + if(!pMN) { + return CMasternode::MASTERNODE_REMOVE; + } + return pMN->nActiveState; +} + +int CMasternodeMan::GetMasternodeState(const CPubKey& pubKeyMasternode) +{ + LOCK(cs); + CMasternode* pMN = Find(pubKeyMasternode); + if(!pMN) { + return CMasternode::MASTERNODE_REMOVE; + } + return pMN->nActiveState; +} + +bool CMasternodeMan::IsMasternodePingedWithin(const CTxIn& vin, int nSeconds, int64_t nTimeToCheckAt) +{ + LOCK(cs); + CMasternode* pMN = Find(vin); + if(!pMN) { + return false; + } + return pMN->IsPingedWithin(nSeconds, nTimeToCheckAt); +} + +void CMasternodeMan::SetMasternodeLastPing(const CTxIn& vin, const CMasternodePing& mnp) +{ + LOCK(cs); + CMasternode* pMN = Find(vin); + if(!pMN) { + return; + } + pMN->lastPing = mnp; + mapSeenMasternodePing.insert(std::make_pair(mnp.GetHash(), mnp)); + + CMasternodeBroadcast mnb(*pMN); + uint256 hash = mnb.GetHash(); + if(mapSeenMasternodeBroadcast.count(hash)) { + mapSeenMasternodeBroadcast[hash].lastPing = mnp; + } +} diff --git a/src/masternodeman.h b/src/masternodeman.h index 82ad3e723..3178f2308 100644 --- a/src/masternodeman.h +++ b/src/masternodeman.h @@ -24,6 +24,8 @@ extern CMasternodeMan mnodeman; class CMasternodeMan { private: + static const std::string SERIALIZATION_VERSION_STRING; + static const int MASTERNODES_LAST_PAID_SCAN_BLOCKS = 100; // critical section to protect the inner data structures @@ -41,6 +43,10 @@ private: // which Masternodes we've asked for std::map mWeAskedForMasternodeListEntry; + std::vector vecDirtyGovernanceObjectHashes; + + int64_t nLastWatchdogVoteTime; + public: // Keep track of all broadcasts I've seen map mapSeenMasternodeBroadcast; @@ -58,14 +64,28 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { LOCK(cs); + std::string strVersion; + if(ser_action.ForRead()) { + READWRITE(strVersion); + } + else { + strVersion = SERIALIZATION_VERSION_STRING; + READWRITE(strVersion); + } + READWRITE(vMasternodes); READWRITE(mAskedUsForMasternodeList); READWRITE(mWeAskedForMasternodeList); READWRITE(mWeAskedForMasternodeListEntry); + READWRITE(nLastWatchdogVoteTime); READWRITE(nDsqCount); READWRITE(mapSeenMasternodeBroadcast); READWRITE(mapSeenMasternodePing); + if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { + LogPrintf("CMasternodeMan::SerializationOp - Incompatible format detected, resetting data\n"); + Clear(); + } } CMasternodeMan(); @@ -86,6 +106,8 @@ public: /// Clear Masternode vector void Clear(); + int CountMasternodes(int protocolVersion = -1); + int CountEnabled(int protocolVersion = -1); /// Count Masternodes by network type - NET_IPV4, NET_IPV6, NET_TOR @@ -102,6 +124,12 @@ public: bool Get(const CPubKey& pubKeyMasternode, CMasternode& masternode); bool Get(const CTxIn& vin, CMasternode& masternode); + bool Has(const CTxIn& vin); + + masternode_info_t GetMasternodeInfo(const CTxIn& vin); + + masternode_info_t GetMasternodeInfo(const CPubKey& pubKeyMasternode); + /// Find an entry in the masternode list that is next to be paid CMasternode* GetNextMasternodeInQueueForPayment(int nBlockHeight, bool fFilterSigTime, int& nCount); @@ -135,6 +163,40 @@ public: bool CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, int& nDos); void UpdateLastPaid(const CBlockIndex *pindex); + + void AddDirtyGovernanceObjectHash(const uint256& nHash) + { + LOCK(cs); + vecDirtyGovernanceObjectHashes.push_back(nHash); + } + + std::vector GetAndClearDirtyGovernanceObjectHashes() + { + LOCK(cs); + std::vector vecTmp = vecDirtyGovernanceObjectHashes; + vecDirtyGovernanceObjectHashes.clear(); + return vecTmp;; + } + + 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); }; #endif diff --git a/src/rpcgovernance.cpp b/src/rpcgovernance.cpp index c5e1ca286..89e3ca164 100644 --- a/src/rpcgovernance.cpp +++ b/src/rpcgovernance.cpp @@ -12,6 +12,7 @@ #include "darksend.h" #include "governance.h" #include "darksend.h" +#include "masternode.h" #include "masternode-payments.h" #include "masternode-sync.h" #include "masternodeconfig.h" @@ -107,8 +108,9 @@ UniValue gobject(const UniValue& params, bool fHelp) CGovernanceObject govobj(hashParent, nRevision, nTime, uint256(), strData); - if(govobj.GetObjectType() == GOVERNANCE_OBJECT_TRIGGER) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Trigger objects need not be prepared (however only masternodes can create them)"); + if((govobj.GetObjectType() == GOVERNANCE_OBJECT_TRIGGER) || + (govobj.GetObjectType() == GOVERNANCE_OBJECT_WATCHDOG)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Trigger and watchdog objects need not be prepared (however only masternodes can create them)"); } std::string strError = ""; @@ -187,7 +189,8 @@ UniValue gobject(const UniValue& params, bool fHelp) << endl; ); // Attempt to sign triggers if we are a MN - if(govobj.GetObjectType() == GOVERNANCE_OBJECT_TRIGGER) { + if((govobj.GetObjectType() == GOVERNANCE_OBJECT_TRIGGER) || + (govobj.GetObjectType() == GOVERNANCE_OBJECT_WATCHDOG)) { if(mnFound) { govobj.SetMasternodeInfo(mn.vin); govobj.Sign(activeMasternode.keyMasternode, activeMasternode.pubKeyMasternode); @@ -773,11 +776,12 @@ UniValue getgovernanceinfo(const UniValue& params, bool fHelp) "Returns an object containing governance parameters.\n" "\nResult:\n" "{\n" - " \"governanceminquorum\": xxxxx, (numeric) the absolute minimum number of votes needed to trigger a governance action\n" - " \"proposalfee\": xxx.xx, (numeric) the collateral transaction fee which must be paid to create a proposal in " + CURRENCY_UNIT + "\n" - " \"superblockcycle\": xxxxx, (numeric) the number of blocks between superblocks\n" - " \"lastsuperblock\": xxxxx, (numeric) the block number of the last superblock\n" - " \"nextsuperblock\": xxxxx, (numeric) the block number of the next superblock\n" + " \"governanceminquorum\": xxxxx, (numeric) the absolute minimum number of votes needed to trigger a governance action\n" + " \"masternodewatchdogmaxseconds\": xxxxx, (numeric) sentinel watchdog expiration time in seconds\n" + " \"proposalfee\": xxx.xx, (numeric) the collateral transaction fee which must be paid to create a proposal in " + CURRENCY_UNIT + "\n" + " \"superblockcycle\": xxxxx, (numeric) the number of blocks between superblocks\n" + " \"lastsuperblock\": xxxxx, (numeric) the block number of the last superblock\n" + " \"nextsuperblock\": xxxxx, (numeric) the block number of the next superblock\n" "}\n" "\nExamples:\n" + HelpExampleCli("getgovernanceinfo", "") @@ -813,6 +817,7 @@ UniValue getgovernanceinfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("governanceminquorum", Params().GetConsensus().nGovernanceMinQuorum)); + obj.push_back(Pair("masternodewatchdogmaxseconds", MASTERNODE_WATCHDOG_MAX_SECONDS)); obj.push_back(Pair("proposalfee", ValueFromAmount(GOVERNANCE_PROPOSAL_FEE_TX))); obj.push_back(Pair("superblockcycle", Params().GetConsensus().nSuperblockCycle)); obj.push_back(Pair("lastsuperblock", nLastSuperblock));