diff --git a/src/Makefile.am b/src/Makefile.am index 9615f89f2ea1a..251cb47971679 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,7 @@ BITCOIN_CORE_H = \ limitedmap.h \ main.h \ masternode.h \ + masternode-pos.h \ masternodeman.h \ masternodeconfig.h \ miner.h \ @@ -154,6 +155,7 @@ libdarkcoin_common_a_SOURCES = \ darksend.cpp \ darksend-relay.cpp \ masternode.cpp \ + masternode-pos.cpp \ masternodeman.cpp \ masternodeconfig.cpp \ instantx.cpp \ diff --git a/src/darksend.cpp b/src/darksend.cpp index c65acac09df2e..92cfc5436ce2f 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -45,7 +45,7 @@ int RequestedMasterNodeList = 0; udjinm6 - udjinm6@darkcoin.io */ -void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +void inline CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if(fLiteMode) return; //disable all Darksend/Masternode related functionality if(IsInitialBlockDownload()) return; diff --git a/src/darksend.h b/src/darksend.h index ecda13022ce2a..f64f717943806 100644 --- a/src/darksend.h +++ b/src/darksend.h @@ -369,7 +369,7 @@ class CDarksendPool * dssub | Darksend Subscribe To * \param vRecv */ - void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + void inline ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); void InitCollateralAddress(){ std::string strAddress = ""; diff --git a/src/main.cpp b/src/main.cpp index 252022316066b..ce1ba32114dba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3242,6 +3242,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl if (!fImporting && !fReindex && chainActive.Height() > Checkpoints::GetTotalBlocksEstimate()){ darkSendPool.NewBlock(); masternodePayments.ProcessBlock(GetHeight()+10); + mnscan.DoMasternodePOSCheck(); } } @@ -3903,6 +3904,8 @@ bool static AlreadyHave(const CInv& inv) return mapSporks.count(inv.hash); case MSG_MASTERNODE_WINNER: return mapSeenMasternodeVotes.count(inv.hash); + case MSG_MASTERNODE_SCANNING_ERROR: + return mapMasternodePosCheck.count(inv.hash); } // Don't know what it is, just say we already got one return true; @@ -4063,6 +4066,15 @@ void static ProcessGetData(CNode* pfrom) pushed = true; } } + if (!pushed && inv.type == MSG_MASTERNODE_SCANNING_ERROR) { + if(mapMasternodePosCheck.count(inv.hash)){ + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapMasternodePosCheck[inv.hash]; + pfrom->PushMessage("mnse", ss); + pushed = true; + } + } if (!pushed) { vNotFound.push_back(inv); @@ -4824,6 +4836,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) ProcessMessageMasternodePayments(pfrom, strCommand, vRecv); ProcessMessageInstantX(pfrom, strCommand, vRecv); ProcessSpork(pfrom, strCommand, vRecv); + ProcessMessageMasternodePOS(pfrom, strCommand, vRecv); } diff --git a/src/masternode-pos.cpp b/src/masternode-pos.cpp new file mode 100644 index 0000000000000..c2a27aa9cccb9 --- /dev/null +++ b/src/masternode-pos.cpp @@ -0,0 +1,234 @@ + + + +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "key.h" +#include "util.h" +#include "script.h" +#include "base58.h" +#include "protocol.h" +#include "activemasternode.h" +#include "masternodeman.h" +#include "spork.h" +#include + +using namespace std; +using namespace boost; + +std::map mapMasternodeScanningErrors; +CMasternodeScanning mnscan; + +/* + Masternode - Proof of Service + + -- What it checks + + 1.) Making sure Masternodes have their ports open + 2.) Are responding to requests made by the network + + -- How it works + + When a block comes in, DoMasternodePOS is executed if the client is a + masternode. Using the deterministic ranking algorithm up to 1% of the masternode + network is checked each block. + + A port is opened from Masternode A to Masternode B, if successful then nothing happens. + If there is an error, a CMasternodeScanningError object is propagated with an error code. + Errors are applied to the Masternodes and a score is incremented within the masternode object, + after a threshold is met, the masternode goes into an error state. Each cycle the score is + decreased, so if the masternode comes back online it will return to the list. + + Masternodes in a error state do not receive payment. + + -- Future expansion + + We want to be able to prove the nodes have many qualities such as a specific CPU speed, bandwidth, + and dedicated storage. E.g. We could require a full node be a computer running 2GHz with 10GB of space. + +*/ + +void inline ProcessMessageMasternodePOS(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if(fLiteMode) return; //disable all darksend/masternode related functionality + if(!IsSporkActive(SPORK_7_MASTERNODE_SCANNING)) return; + if(IsInitialBlockDownload()) return; + + if (strCommand == "mnse") //Masternode Scanning Error + { + LogPrintf("ProcessMessageMasternodePOS::mnse\n"); + CDataStream vMsg(vRecv); + CMasternodeScanningError mnse; + vRecv >> mnse; + + CInv inv(MSG_MASTERNODE_SCANNING_ERROR, mnse.GetHash()); + pfrom->AddInventoryKnown(inv); + + if(mapMasternodeScanningErrors.count(mnse.GetHash())){ + return; + } + + if(!mnse.IsValid()) + { + LogPrintf("MasternodePOS::mnse - Invalid object\n"); + return; + } + + // Lowest masternodes in rank check the highest each block + int n = mnodeman.GetMasternodeRank(mnse.vinMasternodeA, mnse.nBlockHeight, MIN_MASTERNODE_POS_PROTO_VERSION); + if(n > GetCountScanningPerBlock()) + { + LogPrintf("MasternodePOS::mnse - MasternodeA ranking is too high\n"); + return; + } + + int n = mnodeman.GetMasternodeRank(mnse.vinMasternodeB, mnse.nBlockHeight, MIN_MASTERNODE_POS_PROTO_VERSION); + if(n < mnodeman.CountMasternodesAboveProtocol(MIN_MASTERNODE_POS_PROTO_VERSION)-GetCountScanningPerBlock()) + { + LogPrintf("MasternodePOS::mnse - MasternodeB ranking is too low\n"); + return; + } + + if(!CMasternodeScanningError.SignatureValid()){ + LogPrintf("MasternodePOS::mnse - Bad masternode message\n"); + return; + } + + CMasternode* pmn = mnodeman.Find(mnse.vinMasternodeB); + if(pmn == NULL) return; + + pmn.ApplyScanningError(mnse); + } +} + +void CMasternodeScanning::CleanMasternodeScanningErrors() +{ + if(chainActive.Tip() == NULL) return; + + std::map::iterator it = mapMasternodeScanningErrors.begin(); + + while(it != mapMasternodeScanningErrors.end()) { + if(GetTime() > it->second.nExpiration){ //keep them for an hour + LogPrintf("Removing old masternode scanning error %s\n", it->second.GetHash().ToString().c_str()); + + mapMasternodeScanningErrors.erase(it++); + } else { + it++; + } + } + +} + +// Check other masternodes to make sure they're running correctly +void CMasternodeScanning::DoMasternodePOSChecks() +{ + if(!fMasternode) return; + + int a = mnodeman.GetMasternodeRank(activeMasternode.vin, chainActive.Tip()->nHeight, MIN_MASTERNODE_POS_PROTO_VERSION); + if(a > GetCountScanningPerBlock()){ + // we don't need to do anything this block + return; + } + + // The lowest ranking nodes (Masternode A) check the highest ranking nodes (Masternode B) + CMasternode* pmn = mnodeman.GetMasternodeByRank(mnodeman.CountMasternodesAboveProtocol(MIN_MASTERNODE_POS_PROTO_VERSION)-a, chainActive.Tip()->nHeight, MIN_MASTERNODE_POS_PROTO_VERSION); + if(pmn == NULL) return; + + // -- first check : Port is open + + if(!ConnectNode((CAddress)pmn->addr, NULL, true)){ + // we couldn't connect to the node, let's send a scanning error + CMasternodeScanningError mnse(activemasternode.vin, pmn.vin, SCANNING_ERROR_NO_RESPONSE, chainActive.Tip()->nHeight); + mnse.Sign(); + mnse.Relay(); + } + + // -- second check : Responding to IX requests +/* if(false) + { + CMasternodeScanningError mnse(activemasternode.vin, pmn.vin, SCANNING_ERROR_IX_NO_RESPONSE, chainActive.Tip()->nHeight); + mnse.Sign(); + mnse.Relay(); + } +*/ + // success + CMasternodeScanningError mnse(activemasternode.vin, pmn.vin, SCANNING_SUCESS, chainActive.Tip()->nHeight); + mnse.Sign(); + mnse.Relay(); +} + +bool CMasternodeScanningError::SignatureValid() +{ + std::string errorMessage; + std::string strMessage = vinMasternodeB.ToString().c_str() + vinMasternodeB.ToString().c_str() + + boost::lexical_cast(nBlockHeight) + boost::lexical_cast(nErrorType); + + CMasternode* pmn = mnodeman.Find(vinMasternodeA); + + if(pmn == NULL) + { + LogPrintf("CMasternodeScanningError::SignatureValid() - Unknown Masternode\n"); + return false; + } + + CScript pubkey; + pubkey.SetDestination(pmn->pubkey2.GetID()); + CTxDestination address1; + ExtractDestination(pubkey, address1); + CBitcoinAddress address2(address1); + + if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) { + LogPrintf("CMasternodeScanningError::SignatureValid() - Verify message failed\n"); + return false; + } + + return true; +} + +bool CMasternodeScanningError::Sign() +{ + std::string errorMessage; + + CKey key2; + CPubKey pubkey2; + std::string strMessage = vinMasternodeB.ToString().c_str() + vinMasternodeB.ToString().c_str() + + boost::lexical_cast(nBlockHeight) + boost::lexical_cast(nErrorType); + + if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, key2, pubkey2)) + { + LogPrintf("CMasternodeScanningError::Sign() - ERROR: Invalid masternodeprivkey: '%s'\n", errorMessage.c_str()); + return false; + } + + CScript pubkey; + pubkey.SetDestination(pubkey2.GetID()); + CTxDestination address1; + ExtractDestination(pubkey, address1); + CBitcoinAddress address2(address1); + //LogPrintf("signing pubkey2 %s \n", address2.ToString().c_str()); + + if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchMasterNodeSignature, key2)) { + LogPrintf("CMasternodeScanningError::Sign() - Sign message failed"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) { + LogPrintf("CMasternodeScanningError::Sign() - Verify message failed"); + return false; + } + + return true; +} + +bool CMasternodeScanningError::Relay() +{ + CInv inv(MSG_MASTERNODE_SCANNING_ERROR, winner.GetHash()); + + vector vInv; + vInv.push_back(inv); + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes){ + pnode->PushMessage("inv", vInv); + } +} \ No newline at end of file diff --git a/src/masternode-pos.h b/src/masternode-pos.h new file mode 100644 index 0000000000000..8ee1c269e8c82 --- /dev/null +++ b/src/masternode-pos.h @@ -0,0 +1,95 @@ + + +// Copyright (c) 2009-2012 The Darkcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef MASTERNODE_POS_H +#define MASTERNODE_POS_H + +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "key.h" +#include "core.h" +#include "util.h" +#include "script.h" +#include "base58.h" +#include "main.h" + +using namespace std; +using namespace boost; + +class CMasternodePOSCheck; + +extern map mapMasternodePosCheck; +extern CMasternodeScanning mnscan; + +static const int MIN_MASTERNODE_POS_PROTO_VERSION = 70066; + +/* + 1% of the network is scanned every 2.5 minutes, making a full + round of scanning take about 4.16 hours. We're targeting about + a day of proof-of-service errors for complete removal from the + masternode system. +*/ +static const int MASTERNODE_SCANNING_ERROR_THESHOLD = 6; + +#define SCANNING_SUCCESS 1 +#define SCANNING_ERROR_NO_RESPONSE 2 +#define SCANNING_ERROR_IX_NO_RESPONSE 3 +#define SCANNING_ERROR_MAX 3 + +void inline ProcessMessageMasternodePOS(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + +class CMasternodeScanning +{ +public: + void DoMasternodePOSChecks(); + void CleanMasternodeScanningErrors(); +} + +// Returns how many masternodes are allowed to scan each block +int GetCountScanningPerBlock() +{ + return max(1, mnodeman.CountMasternodesAboveProtocol(MIN_MASTERNODE_POS_PROTO_VERSION)*0.01); +} + +class CMasternodeScanningError +{ +public: + CTxIn vinMasternodeA; + CTxIn vinMasternodeB; + int nErrorType; + int nExpiration; + int nBlockHeight; + std::vector vchMasterNodeSignature; + + CMasternodeScanningError::CMasternodeScanningError (CTxIn vinMasternodeAIn, CTxIn vinMasternodeBIn, int nErrorTypeIn, int nBlockHeightIn) + { + vinMasternodeA = vinMasternodeAIn; + vinMasternodeB = vinMasternodeBIn; + nErrorType = nErrorTypeIn; + nExpiration = GetTime()+(60*60); + nBlockHeight = nBlockHeightIn; + } + + uint256 GetHash() const {return SerializeHash(*this);} + + bool SignatureValid(); + bool Sign(); + bool IsExpired() {return GetTime() > nExpiration;} + void Relay(); + void IsValid() { + return nErrorType > 0 && nErrorType <= SCANNING_ERROR_MAX; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(vinMasternodeA); + READWRITE(vinMasternodeB); + READWRITE(nErrorType); + READWRITE(nExpiration); + READWRITE(nBlockHeight); + READWRITE(vchMasterNodeSignature); + ) +}; diff --git a/src/masternode.cpp b/src/masternode.cpp index 2dccd7b53632d..22783926bd8b9 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -19,7 +19,7 @@ map mapSeenMasternodeScanningErrors; // cache block hashes as we calculate them std::map mapCacheBlockHashes; -void ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +void inline ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if(IsInitialBlockDownload()) return; @@ -210,6 +210,12 @@ void CMasternode::Check() { LOCK(cs_main); + if(activeState != MASTERNODE_POS_ERROR && nScanningErrorCount > MASTERNODE_SCANNING_ERROR_THESHOLD) + { + activeState = MASTERNODE_POS_ERROR; + return; + } + //once spent, stop doing the checks if(activeState == MASTERNODE_VIN_SPENT) return; diff --git a/src/masternode.h b/src/masternode.h index a932530728cc3..0ce9a6eb0b705 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -47,10 +47,11 @@ enum masternodeState { MASTERNODE_ENABLED = 1, MASTERNODE_EXPIRED = 2, MASTERNODE_VIN_SPENT = 3, - MASTERNODE_REMOVE = 4 + MASTERNODE_REMOVE = 4, + MASTERNODE_POS_ERROR = 5 }; -void ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); +void inline ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); // // The Masternode Class. For managing the Darksend process. It contains the input of the 1000DRK, signature to prove @@ -78,6 +79,7 @@ class CMasternode bool allowFreeTx; int protocolVersion; int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node + int nScanningErrorCount; CMasternode(); CMasternode(const CMasternode& other); @@ -196,6 +198,18 @@ class CMasternode return cacheInputAge+(chainActive.Tip()->nHeight-cacheInputAgeBlock); } + + void ApplyScanningError(CMasternodeScanningError& mnse) + { + if(!mnse.IsValid()) return; + + if(mnse.nErrorType == SCANNING_SUCCESS){ + nScanningErrorCount--; + } else { //all other codes are equally as bad + nScanningErrorCount++; + } + } + }; // for storing the winning payments diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 3379f4c612b9f..8e94d43c347fc 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -468,7 +468,7 @@ void CMasternodeMan::RelayMasternodeEntryPing(const CTxIn vin, const std::vector pnode->PushMessage("dseep", vin, vchSig, nNow, stop); } -void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +void inline CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if(fLiteMode) return; //disable all Darksend/Masternode related functionality diff --git a/src/masternodeman.h b/src/masternodeman.h index 0327c34712576..556cb440b6702 100644 --- a/src/masternodeman.h +++ b/src/masternodeman.h @@ -121,7 +121,7 @@ class CMasternodeMan int GetMasternodeRank(const CTxIn &vin, int64_t nBlockHeight, int minProtocol=0); CMasternode* GetMasternodeByRank(int nRank, int64_t nBlockHeight, int minProtocol=0); - void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + void inline ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); void ProcessMasternodeConnections(); diff --git a/src/protocol.cpp b/src/protocol.cpp index 13e6ae35903fe..c42916d40858b 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -25,6 +25,7 @@ static const char* ppszTypeName[] = "unknown", "unknown", "unknown", + "unknown", "unknown" }; diff --git a/src/protocol.h b/src/protocol.h index 163a94721e06a..4d40fd84707c6 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -138,7 +138,8 @@ enum MSG_TXLOCK_REQUEST, MSG_TXLOCK_VOTE, MSG_SPORK, - MSG_MASTERNODE_WINNER + MSG_MASTERNODE_WINNER, + MSG_MASTERNODE_SCANNING_ERROR }; #endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/spork.cpp b/src/spork.cpp index e815266adb3a0..c9cd6b92953c0 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -87,7 +87,7 @@ bool IsSporkActive(int nSporkID) if(nSporkID == SPORK_2_INSTANTX) r = SPORK_2_INSTANTX_DEFAULT; if(nSporkID == SPORK_3_INSTANTX_BLOCK_FILTERING) r = SPORK_3_INSTANTX_BLOCK_FILTERING_DEFAULT; if(nSporkID == SPORK_5_MAX_VALUE) r = SPORK_5_MAX_VALUE_DEFAULT; - if(nSporkID == SPORK_6_REPLAY_BLOCKS) r = SPORK_6_REPLAY_BLOCKS_DEFAULT; + if(nSporkID == SPORK_7_MASTERNODE_SCANNING) r = SPORK_7_MASTERNODE_SCANNING; if(r == 0) LogPrintf("GetSpork::Unknown Spork %d\n", nSporkID); } @@ -108,7 +108,7 @@ int GetSporkValue(int nSporkID) if(nSporkID == SPORK_2_INSTANTX) r = SPORK_2_INSTANTX_DEFAULT; if(nSporkID == SPORK_3_INSTANTX_BLOCK_FILTERING) r = SPORK_3_INSTANTX_BLOCK_FILTERING_DEFAULT; if(nSporkID == SPORK_5_MAX_VALUE) r = SPORK_5_MAX_VALUE_DEFAULT; - if(nSporkID == SPORK_6_REPLAY_BLOCKS) r = SPORK_6_REPLAY_BLOCKS_DEFAULT; + if(nSporkID == SPORK_7_MASTERNODE_SCANNING) r = SPORK_7_MASTERNODE_SCANNING; if(r == 0) LogPrintf("GetSpork::Unknown Spork %d\n", nSporkID); } @@ -118,10 +118,6 @@ int GetSporkValue(int nSporkID) void ExecuteSpork(int nSporkID, int nValue) { - //replay and process blocks (to sync to the longest chain after disabling sporks) - if(nSporkID == SPORK_6_REPLAY_BLOCKS){ - DisconnectBlocksAndReprocess(nValue); - } } @@ -220,7 +216,7 @@ int CSporkManager::GetSporkIDByName(std::string strName) if(strName == "SPORK_2_INSTANTX") return SPORK_2_INSTANTX; if(strName == "SPORK_3_INSTANTX_BLOCK_FILTERING") return SPORK_3_INSTANTX_BLOCK_FILTERING; if(strName == "SPORK_5_MAX_VALUE") return SPORK_5_MAX_VALUE; - if(strName == "SPORK_6_REPLAY_BLOCKS") return SPORK_6_REPLAY_BLOCKS; + if(strName == "SPORK_7_MASTERNODE_SCANNING") return SPORK_7_MASTERNODE_SCANNING; return -1; } @@ -231,7 +227,7 @@ std::string CSporkManager::GetSporkNameByID(int id) if(id == SPORK_2_INSTANTX) return "SPORK_2_INSTANTX"; if(id == SPORK_3_INSTANTX_BLOCK_FILTERING) return "SPORK_3_INSTANTX_BLOCK_FILTERING"; if(id == SPORK_5_MAX_VALUE) return "SPORK_5_MAX_VALUE"; - if(id == SPORK_6_REPLAY_BLOCKS) return "SPORK_6_REPLAY_BLOCKS"; + if(id == SPORK_7_MASTERNODE_SCANNING) return "SPORK_7_MASTERNODE_SCANNING"; return "Unknown"; } \ No newline at end of file diff --git a/src/spork.h b/src/spork.h index 2467f2434a69f..4c59b3d29e558 100644 --- a/src/spork.h +++ b/src/spork.h @@ -24,14 +24,16 @@ using namespace boost; #define SPORK_3_INSTANTX_BLOCK_FILTERING 10002 #define SPORK_4_NOTUSED 10003 #define SPORK_5_MAX_VALUE 10004 -#define SPORK_6_REPLAY_BLOCKS 10005 +#define SPORK_6_NOTUSED 10005 +#define SPORK_7_MASTERNODE_SCANNING 10006 #define SPORK_1_MASTERNODE_PAYMENTS_ENFORCEMENT_DEFAULT 1424217600 //2015-2-18 #define SPORK_2_INSTANTX_DEFAULT 978307200 //2001-1-1 #define SPORK_3_INSTANTX_BLOCK_FILTERING_DEFAULT 1424217600 //2015-2-18 -#define SPORK_4_RECONVERGE_DEFAULT 1420070400 //2047-1-1 +#define SPORK_4_NOTUSED 1420070400 //2047-1-1 #define SPORK_5_MAX_VALUE_DEFAULT 1000 //1000 DRK -#define SPORK_6_REPLAY_BLOCKS_DEFAULT 0 +#define SPORK_6_NOTUSED 0 +#define SPORK_2_MASTERNODE_SCANNING 978307200 //2001-1-1 class CSporkMessage; class CSporkManager;