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
This commit is contained in:
Tim Flynn 2016-10-17 14:54:28 -04:00 committed by UdjinM6
parent a584a68634
commit 94e38e3184
14 changed files with 774 additions and 276 deletions

View File

@ -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;
}
}

View File

@ -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

View File

@ -295,6 +295,7 @@ std::vector<CSuperblock_sptr> 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<CSuperblock_sptr> 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";
}

View File

@ -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; }

View File

@ -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<std::string>(nVoteSignal) + "|" + boost::lexical_cast<std::string>(nVoteOutcome) + "|" + boost::lexical_cast<std::string>(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;
}

View File

@ -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();
}
/**

View File

@ -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<uint256> 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;
}
}

View File

@ -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);
};

View File

@ -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();

View File

@ -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<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.find(nGovernanceObjectHash);
if(it == mapGovernanceObjectsVotedOn.end()) {
return;
}
mapGovernanceObjectsVotedOn.erase(it);
}
void CMasternode::UpdateWatchdogVoteTime()
{
LOCK(cs);
nTimeLastWatchdogVote = GetTime();
}
/**
* FLAG GOVERNANCE ITEMS AS DIRTY
*
* - When masternode come and go on the network, we must flag the items they voted on to recalc it's cached flags
*
*/
void CMasternode::FlagGovernanceItemsAsDirty()
{
std::map<uint256, int>::iterator it = mapGovernaceObjectsVotedOn.begin();
while(it != mapGovernaceObjectsVotedOn.end()){
CGovernanceObject *pObj = governance.FindGovernanceObject((*it).first);
if(pObj) pObj->fDirtyCache = true;
++it;
std::vector<uint256> vecDirty;
{
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();
while(it != mapGovernanceObjectsVotedOn.end()) {
vecDirty.push_back(it->first);
++it;
}
}
for(size_t i = 0; i < vecDirty.size(); ++i) {
mnodeman.AddDirtyGovernanceObjectHash(vecDirty[i]);
}
}

View File

@ -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<uint256, int> mapGovernaceObjectsVotedOn;
std::map<uint256, int> 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)
{

View File

@ -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;
}
}

View File

@ -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<COutPoint, int64_t> mWeAskedForMasternodeListEntry;
std::vector<uint256> vecDirtyGovernanceObjectHashes;
int64_t nLastWatchdogVoteTime;
public:
// Keep track of all broadcasts I've seen
map<uint256, CMasternodeBroadcast> mapSeenMasternodeBroadcast;
@ -58,14 +64,28 @@ public:
template <typename Stream, typename Operation>
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<uint256> GetAndClearDirtyGovernanceObjectHashes()
{
LOCK(cs);
std::vector<uint256> 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

View File

@ -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));