diff --git a/src/Makefile.am b/src/Makefile.am index 31c6849e3433f..6c0713dd93be1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,6 +52,7 @@ BITCOIN_CORE_H = \ limitedmap.h \ main.h \ masternode.h \ + masternodeman.h \ masternodeconfig.h \ miner.h \ mruset.h \ @@ -151,6 +152,7 @@ libdarkcoin_common_a_SOURCES = \ core.cpp \ darksend.cpp \ masternode.cpp \ + masternodeman.cpp \ masternodeconfig.cpp \ instantx.cpp \ hash.cpp \ diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index 3743b0cbc09f4..d7b48900ad85e 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -2,6 +2,7 @@ #include "core.h" #include "protocol.h" #include "activemasternode.h" +#include "masternodeman.h" #include // @@ -200,16 +201,13 @@ bool CActiveMasternode::Dseep(CTxIn vin, CService service, CKey keyMasternode, C } // Update Last Seen timestamp in masternode list - bool found = false; - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { - //LogPrintf(" -- %s\n", mn.vin.ToString().c_str()); - if(mn.vin == vin) { - found = true; - mn.UpdateLastSeen(); - } + CMasternode* pmn = mnodeman.Find(vin); + if(pmn != NULL) + { + pmn->UpdateLastSeen(); } - - if(!found){ + else + { // Seems like we are trying to send a ping while the masternode is not registered in the network retErrorMessage = "Darksend Masternode List doesn't include our masternode, Shutting down masternode pinging service! " + vin.ToString(); LogPrintf("CActiveMasternode::Dseep() - Error: %s\n", retErrorMessage.c_str()); @@ -269,16 +267,13 @@ bool CActiveMasternode::Register(CTxIn vin, CService service, CKey keyCollateral return false; } - bool found = false; - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) - if(mn.vin == vin) - found = true; - - if(!found) { + CMasternode* pmn = mnodeman.Find(vin); + if(pmn == NULL) + { LogPrintf("CActiveMasternode::Register() - Adding to masternode list service: %s - vin: %s\n", service.ToString().c_str(), vin.ToString().c_str()); - CMasterNode mn(service, vin, pubKeyCollateralAddress, vchMasterNodeSignature, masterNodeSignatureTime, pubKeyMasternode, PROTOCOL_VERSION); + CMasternode mn(service, vin, pubKeyCollateralAddress, vchMasterNodeSignature, masterNodeSignatureTime, pubKeyMasternode, PROTOCOL_VERSION); mn.UpdateLastSeen(masterNodeSignatureTime); - vecMasternodes.push_back(mn); + mnodeman.Add(mn); } //send to all peers @@ -378,32 +373,6 @@ vector CActiveMasternode::SelectCoinsMasternode() return filteredCoins; } - -/* select coins with specified transaction hash and output index */ -/* -bool CActiveMasternode::SelectCoinsMasternode(CTxIn& vin, int64& nValueIn, CScript& pubScript, std::string strTxHash, std::string strOutputIndex) -{ - CWalletTx ctx; - - // Convert configuration strings - uint256 txHash; - int outputIndex; - txHash.SetHex(strTxHash); - std::istringstream(strOutputIndex) >> outputIndex; - - if(pwalletMain->GetTransaction(txHash, ctx)) { - if(ctx.vout[outputIndex].nValue == 1000*COIN) { //exactly - vin = CTxIn(ctx.GetHash(), outputIndex); - pubScript = ctx.vout[outputIndex].scriptPubKey; // the inputs PubKey - nValueIn = ctx.vout[outputIndex].nValue; - return true; - } - } - - return false; -} -*/ - // when starting a masternode, this can enable to run as a hot wallet with no funds bool CActiveMasternode::EnableHotColdMasterNode(CTxIn& newVin, CService& newService) { diff --git a/src/activemasternode.h b/src/activemasternode.h index 2b118f4d71c1a..abecfe95f7e0d 100644 --- a/src/activemasternode.h +++ b/src/activemasternode.h @@ -52,8 +52,6 @@ class CActiveMasternode vector SelectCoinsMasternode(); bool GetVinFromOutput(COutput out, CTxIn& vin, CPubKey& pubkey, CKey& secretKey); - //bool SelectCoinsMasternode(CTxIn& vin, int64& nValueIn, CScript& pubScript, std::string strTxHash, std::string strOutputIndex); - // enable hot wallet mode (run a masternode with no funds) bool EnableHotColdMasterNode(CTxIn& vin, CService& addr); }; diff --git a/src/darksend.cpp b/src/darksend.cpp index 62f48a810c58d..37b1ee94380bd 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -10,7 +10,7 @@ #include "main.h" #include "init.h" #include "util.h" -#include "masternode.h" +#include "masternodeman.h" #include "instantx.h" #include "ui_interface.h" @@ -121,17 +121,18 @@ void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv >> nDenom >> txCollateral; std::string error = ""; - int mn = GetMasternodeByVin(activeMasternode.vin); - if(mn == -1){ + CMasternode* pmn = mnodeman.Find(activeMasternode.vin); + if(pmn == NULL) + { std::string strError = _("Not in the masternode list."); pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, strError); return; } if(darkSendPool.sessionUsers == 0) { - if(vecMasternodes[mn].nLastDsq != 0 && - vecMasternodes[mn].nLastDsq + CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ - //LogPrintf("dsa -- last dsq too recent, must wait. %s \n", vecMasternodes[mn].addr.ToString().c_str()); + if(pmn->nLastDsq != 0 && + pmn->nLastDsq + mnodeman.CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ + LogPrintf("dsa -- last dsq too recent, must wait. %s \n", pmn->addr.ToString().c_str()); std::string strError = _("Last Darksend was too recent."); pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, strError); return; @@ -164,8 +165,8 @@ void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& if(dsq.IsExpired()) return; - int mn = GetMasternodeByVin(dsq.vin); - if(mn == -1) return; + CMasternode* pmn = mnodeman.Find(dsq.vin); + if(pmn == NULL) return; // if the queue is ready, submit if we can if(dsq.ready) { @@ -181,16 +182,16 @@ void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& if(q.vin == dsq.vin) return; } - if(fDebug) LogPrintf("dsq last %d last2 %d count %d\n", vecMasternodes[mn].nLastDsq, vecMasternodes[mn].nLastDsq + (int)vecMasternodes.size()/5, darkSendPool.nDsqCount); + if(fDebug) LogPrintf("dsq last %d last2 %d count %d\n", pmn->nLastDsq, pmn->nLastDsq + mnodeman.size()/5, darkSendPool.nDsqCount); //don't allow a few nodes to dominate the queuing process - if(vecMasternodes[mn].nLastDsq != 0 && - vecMasternodes[mn].nLastDsq + CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ - if(fDebug) LogPrintf("dsq -- masternode sending too many dsq messages. %s \n", vecMasternodes[mn].addr.ToString().c_str()); + if(pmn->nLastDsq != 0 && + pmn->nLastDsq + mnodeman.CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ + if(fDebug) LogPrintf("dsq -- masternode sending too many dsq messages. %s \n", pmn->addr.ToString().c_str()); return; } darkSendPool.nDsqCount++; - vecMasternodes[mn].nLastDsq = darkSendPool.nDsqCount; - vecMasternodes[mn].allowFreeTx = true; + pmn->nLastDsq = darkSendPool.nDsqCount; + pmn->allowFreeTx = true; if(fDebug) LogPrintf("dsq - new darksend queue object - %s\n", addr.ToString().c_str()); vecDarksendQueue.push_back(dsq); @@ -484,6 +485,23 @@ int GetInputDarksendRounds(CTxIn in, int rounds) return rounds-1; } +// manage the masternode connections +void CDarkSendPool::ProcessMasternodeConnections() +{ + LOCK(cs_vNodes); + + BOOST_FOREACH(CNode* pnode, vNodes) + { + //if it's our masternode, let it be + if(submittedToMasternode == pnode->addr) continue; + + if(pnode->fDarkSendMaster){ + LogPrintf("Closing masternode connection %s \n", pnode->addr.ToString().c_str()); + pnode->CloseSocketDisconnect(); + } + } +} + void CDarkSendPool::Reset(){ cachedLastSuccess = 0; vecMasternodesUsed.clear(); @@ -1357,7 +1375,6 @@ void CDarkSendPool::NewBlock() if(!fMasterNode){ //denominate all non-denominated inputs every 25 minutes. if(chainActive.Tip()->nHeight % 10 == 0) UnlockCoins(); - ProcessMasternodeConnections(); } } @@ -1426,7 +1443,7 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready) } } - if(vecMasternodes.size() == 0){ + if(mnodeman.size() == 0){ if(fDebug) LogPrintf("CDarkSendPool::DoAutomaticDenominating - No masternodes detected\n"); strAutoDenomResult = _("No masternodes detected."); return false; @@ -1579,40 +1596,39 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready) } } - //shuffle masternodes around before we try to connect - std::random_shuffle ( vecMasternodes.begin(), vecMasternodes.end() ); int i = 0; // otherwise, try one randomly while(i < 10) { + CMasternode* mn = mnodeman.FindRandom(); //don't reuse masternodes BOOST_FOREACH(CTxIn usedVin, vecMasternodesUsed) { - if(vecMasternodes[i].vin == usedVin){ + if(mn->vin == usedVin){ i++; continue; } } - if(vecMasternodes[i].protocolVersion < MIN_PEER_PROTO_VERSION) { + if(mn->protocolVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { i++; continue; } - if(vecMasternodes[i].nLastDsq != 0 && - vecMasternodes[i].nLastDsq + CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ + if(mn->nLastDsq != 0 && + mn->nLastDsq + mnodeman.CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ i++; continue; } lastTimeChanged = GetTimeMillis(); - LogPrintf("DoAutomaticDenominating -- attempt %d connection to masternode %s\n", i, vecMasternodes[i].addr.ToString().c_str()); - if(ConnectNode((CAddress)vecMasternodes[i].addr, NULL, true)){ - submittedToMasternode = vecMasternodes[i].addr; + submittedToMasternode = mn->addr; + LogPrintf("DoAutomaticDenominating -- attempt %d connection to masternode %s\n", i, mn->addr.ToString().c_str()); + if(ConnectNode((CAddress)mn->addr, NULL, true)){ LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { - if((CNetAddr)pnode->addr != (CNetAddr)vecMasternodes[i].addr) continue; + if((CNetAddr)pnode->addr != (CNetAddr)mn->addr) continue; std::string strReason; if(txCollateral == CTransaction()){ @@ -1622,7 +1638,7 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready) } } - vecMasternodesUsed.push_back(vecMasternodes[i].vin); + vecMasternodesUsed.push_back(mn->vin); std::vector vecAmounts; pwalletMain->ConvertList(vCoins, vecAmounts); @@ -2115,18 +2131,18 @@ bool CDarksendQueue::Relay() bool CDarksendQueue::CheckSignature() { - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { + CMasternode* pmn = mnodeman.Find(vin); - if(mn.vin == vin) { - std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(time) + boost::lexical_cast(ready); - - std::string errorMessage = ""; - if(!darkSendSigner.VerifyMessage(mn.pubkey2, vchSig, strMessage, errorMessage)){ - return error("CDarksendQueue::CheckSignature() - Got bad masternode address signature %s \n", vin.ToString().c_str()); - } + if(pmn != NULL) + { + std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(time) + boost::lexical_cast(ready); - return true; + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)){ + return error("CDarksendQueue::CheckSignature() - Got bad masternode address signature %s \n", vin.ToString().c_str()); } + + return true; } return false; @@ -2152,36 +2168,24 @@ void ThreadCheckDarkSendPool() //LogPrintf("ThreadCheckDarkSendPool::check timeout\n"); darkSendPool.CheckTimeout(); - if(c % 60 == 0){ + if(c % 60 == 0) + { LOCK(cs_main); /* - cs_main is required for doing masternode.Check because something + cs_main is required for doing CMasternode.Check because something is modifying the coins view without a mempool lock. It causes segfaults from this code without the cs_main lock. */ - - vector::iterator it = vecMasternodes.begin(); - //check them separately - while(it != vecMasternodes.end()){ - (*it).Check(); - ++it; - } - - //remove inactive - it = vecMasternodes.begin(); - while(it != vecMasternodes.end()){ - if((*it).enabled == 4 || (*it).enabled == 3){ - LogPrintf("Removing inactive masternode %s\n", (*it).addr.ToString().c_str()); - it = vecMasternodes.erase(it); - } else { - ++it; - } - } - + mnodeman.CheckAndRemove(); + darkSendPool.ProcessMasternodeConnections(); masternodePayments.CleanPaymentList(); CleanTransactionLocksList(); } + if(c % MASTERNODE_PING_SECONDS == 0) activeMasternode.ManageStatus(); + + if(c % MASTERNODES_DUMP_SECONDS == 0) DumpMasternodes(); + //try to sync the masternode list and payment list every 5 seconds from at least 3 nodes if(c % 5 == 0 && RequestedMasterNodeList < 3){ bool fIsInitialDownload = IsInitialBlockDownload(); @@ -2197,7 +2201,9 @@ void ThreadCheckDarkSendPool() LogPrintf("Successfully synced, asking for Masternode list and payment list\n"); - pnode->PushMessage("dseg", CTxIn()); //request full mn list + //request full mn list only if masternodes.dat was updated quite a long time ago + mnodeman.DsegUpdate(pnode); + pnode->PushMessage("mnget"); //sync payees pnode->PushMessage("getsporks"); //get current network sporks RequestedMasterNodeList++; @@ -2206,13 +2212,9 @@ void ThreadCheckDarkSendPool() } } - if(c % MASTERNODE_PING_SECONDS == 0){ - activeMasternode.ManageStatus(); - } - if(c % 60 == 0){ //if we've used 1/5 of the masternode list, then clear the list. - if((int)vecMasternodesUsed.size() > (int)vecMasternodes.size() / 5) + if((int)vecMasternodesUsed.size() > (int)mnodeman.size() / 5) vecMasternodesUsed.clear(); } diff --git a/src/darksend.h b/src/darksend.h index a0d727d7bed83..2960ea896a8c0 100644 --- a/src/darksend.h +++ b/src/darksend.h @@ -7,8 +7,8 @@ #include "core.h" #include "main.h" -#include "masternode.h" #include "activemasternode.h" +#include "masternodeman.h" class CTxIn; class CDarkSendPool; @@ -157,22 +157,22 @@ class CDarksendQueue bool GetAddress(CService &addr) { - BOOST_FOREACH(CMasterNode mn, vecMasternodes) { - if(mn.vin == vin){ - addr = mn.addr; - return true; - } + CMasternode* pmn = mnodeman.Find(vin); + if(pmn != NULL) + { + addr = pmn->addr; + return true; } return false; } bool GetProtocolVersion(int &protocolVersion) { - BOOST_FOREACH(CMasterNode mn, vecMasternodes) { - if(mn.vin == vin){ - protocolVersion = mn.protocolVersion; - return true; - } + CMasternode* pmn = mnodeman.Find(vin); + if(pmn != NULL) + { + protocolVersion = pmn->protocolVersion; + return true; } return false; } @@ -287,6 +287,8 @@ class CDarkSendPool SetNull(); } + void ProcessMasternodeConnections(); + void InitCollateralAddress(){ std::string strAddress = ""; if(Params().NetworkID() == CChainParams::MAIN) { diff --git a/src/init.cpp b/src/init.cpp index b25c6d348b94f..b588d5d3edf57 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -21,6 +21,7 @@ #include "ui_interface.h" #include "util.h" #include "activemasternode.h" +#include "masternodeman.h" #include "spork.h" #ifdef ENABLE_WALLET #include "db.h" @@ -147,6 +148,7 @@ void Shutdown() GenerateBitcoins(false, NULL, 0); #endif StopNode(); + DumpMasternodes(); UnregisterNodeSignals(GetNodeSignals()); { LOCK(cs_main); @@ -1150,6 +1152,20 @@ bool AppInit2(boost::thread_group& threadGroup) //CAddress addr; //ConnectNode(addr, strNode.c_str(), true); + uiInterface.InitMessage(_("Loading masternode list...")); + + nStart = GetTimeMillis(); + + { + CMasternodeDB mndb; + if (!mndb.Read(mnodeman)) + LogPrintf("Invalid or missing masternodes.dat; recreating\n"); + } + + LogPrintf("Loaded %i masternodes from masternodes.dat %dms\n", + mnodeman.size(), GetTimeMillis() - nStart); + + fMasterNode = GetBoolArg("-masternode", false); if(fMasterNode) { LogPrintf("IS DARKSEND MASTER NODE\n"); diff --git a/src/instantx.cpp b/src/instantx.cpp index 070602f5bc3a6..69dab96d82a1a 100644 --- a/src/instantx.cpp +++ b/src/instantx.cpp @@ -10,8 +10,8 @@ #include "base58.h" #include "protocol.h" #include "instantx.h" -#include "masternode.h" #include "activemasternode.h" +#include "masternodeman.h" #include "darksend.h" #include "spork.h" #include @@ -259,7 +259,7 @@ void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight) { if(!fMasterNode) return; - int n = GetMasternodeRank(activeMasternode.vin, nBlockHeight, MIN_INSTANTX_PROTO_VERSION); + int n = mnodeman.GetMasternodeRank(activeMasternode.vin, nBlockHeight, MIN_INSTANTX_PROTO_VERSION); if(n == -1) { @@ -307,11 +307,12 @@ void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight) //received a consensus vote bool ProcessConsensusVote(CConsensusVote& ctx) { - int n = GetMasternodeRank(ctx.vinMasternode, ctx.nBlockHeight, MIN_INSTANTX_PROTO_VERSION); + int n = mnodeman.GetMasternodeRank(ctx.vinMasternode, ctx.nBlockHeight, MIN_INSTANTX_PROTO_VERSION); - int x = GetMasternodeByVin(ctx.vinMasternode); - if(x != -1){ - if(fDebug) LogPrintf("InstantX::ProcessConsensusVote - Masternode ADDR %s %d\n", vecMasternodes[x].addr.ToString().c_str(), n); + CMasternode* pmn = mnodeman.Find(ctx.vinMasternode); + if(pmn != NULL) + { + if(fDebug) LogPrintf("InstantX::ProcessConsensusVote - Masternode ADDR %s %d\n", pmn->addr.ToString().c_str(), n); } if(n == -1) @@ -480,9 +481,9 @@ bool CConsensusVote::SignatureValid() std::string strMessage = txHash.ToString().c_str() + boost::lexical_cast(nBlockHeight); //LogPrintf("verify strMessage %s \n", strMessage.c_str()); - int n = GetMasternodeByVin(vinMasternode); + CMasternode* pmn = mnodeman.Find(vinMasternode); - if(n == -1) + if(pmn == NULL) { LogPrintf("InstantX::CConsensusVote::SignatureValid() - Unknown Masternode\n"); return false; @@ -493,13 +494,13 @@ bool CConsensusVote::SignatureValid() //LogPrintf("verify addr %d %s \n", n, vecMasternodes[n].addr.ToString().c_str()); CScript pubkey; - pubkey.SetDestination(vecMasternodes[n].pubkey2.GetID()); + pubkey.SetDestination(pmn->pubkey2.GetID()); CTxDestination address1; ExtractDestination(pubkey, address1); CBitcoinAddress address2(address1); //LogPrintf("verify pubkey2 %s \n", address2.ToString().c_str()); - if(!darkSendSigner.VerifyMessage(vecMasternodes[n].pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) { + if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) { LogPrintf("InstantX::CConsensusVote::SignatureValid() - Verify message failed\n"); return false; } @@ -549,7 +550,7 @@ bool CTransactionLock::SignaturesValid() BOOST_FOREACH(CConsensusVote vote, vecConsensusVotes) { - int n = GetMasternodeRank(vote.vinMasternode, vote.nBlockHeight, MIN_INSTANTX_PROTO_VERSION); + int n = mnodeman.GetMasternodeRank(vote.vinMasternode, vote.nBlockHeight, MIN_INSTANTX_PROTO_VERSION); if(n == -1) { diff --git a/src/main.cpp b/src/main.cpp index ecfdb937be059..550b51beab5ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,7 @@ #include "init.h" #include "instantx.h" #include "darksend.h" -#include "masternode.h" +#include "masternodeman.h" #include "net.h" #include "txdb.h" #include "txmempool.h" @@ -4457,37 +4457,37 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) //these allow masternodes to publish a limited amount of free transactions vRecv >> tx >> vin >> vchSig >> sigTime; - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { - if(mn.vin == vin) { - if(!mn.allowFreeTx){ - //multiple peers can send us a valid masternode transaction - if(fDebug) LogPrintf("dstx: Masternode sending too many transactions %s\n", tx.GetHash().ToString().c_str()); - return true; - } + CMasternode* pmn = mnodeman.Find(vin); + if(pmn != NULL) + { + if(!pmn->allowFreeTx){ + //multiple peers can send us a valid masternode transaction + if(fDebug) LogPrintf("dstx: Masternode sending too many transactions %s\n", tx.GetHash().ToString().c_str()); + return true; + } - std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); + std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); - std::string errorMessage = ""; - if(!darkSendSigner.VerifyMessage(mn.pubkey2, vchSig, strMessage, errorMessage)){ - LogPrintf("dstx: Got bad masternode address signature %s \n", vin.ToString().c_str()); - //pfrom->Misbehaving(20); - return false; - } + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)){ + LogPrintf("dstx: Got bad masternode address signature %s \n", vin.ToString().c_str()); + //pfrom->Misbehaving(20); + return false; + } - LogPrintf("dstx: Got Masternode transaction %s\n", tx.GetHash().ToString().c_str()); + LogPrintf("dstx: Got Masternode transaction %s\n", tx.GetHash().ToString().c_str()); - allowFree = true; - mn.allowFreeTx = false; + allowFree = true; + pmn->allowFreeTx = false; - if(!mapDarksendBroadcastTxes.count(tx.GetHash())){ - CDarksendBroadcastTx dstx; - dstx.tx = tx; - dstx.vin = vin; - dstx.vchSig = vchSig; - dstx.sigTime = sigTime; + if(!mapDarksendBroadcastTxes.count(tx.GetHash())){ + CDarksendBroadcastTx dstx; + dstx.tx = tx; + dstx.vin = vin; + dstx.vchSig = vchSig; + dstx.sigTime = sigTime; - mapDarksendBroadcastTxes.insert(make_pair(tx.GetHash(), dstx)); - } + mapDarksendBroadcastTxes.insert(make_pair(tx.GetHash(), dstx)); } } } @@ -4825,7 +4825,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { //probably one the extensions ProcessMessageDarksend(pfrom, strCommand, vRecv); - ProcessMessageMasternode(pfrom, strCommand, vRecv); + mnodeman.ProcessMessage(pfrom, strCommand, vRecv); ProcessMessageInstantX(pfrom, strCommand, vRecv); ProcessSpork(pfrom, strCommand, vRecv); } diff --git a/src/masternode.cpp b/src/masternode.cpp index 5b8af05af416d..65ee0f44e3180 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -1,321 +1,27 @@ +// Copyright (c) 2014-2015 The Darkcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "masternode.h" -#include "activemasternode.h" +#include "masternodeman.h" #include "darksend.h" #include "core.h" #include "util.h" #include "addrman.h" #include -/** The list of active masternodes */ -std::vector vecMasternodes; /** Object for who's going to get paid on which blocks */ CMasternodePayments masternodePayments; // keep track of masternode votes I've seen map mapSeenMasternodeVotes; // keep track of the scanning errors I've seen map mapSeenMasternodeScanningErrors; -// who's asked for the masternode list and the last time -std::map askedForMasternodeList; -// which masternodes we've asked for -std::map askedForMasternodeListEntry; // cache block hashes as we calculate them std::map mapCacheBlockHashes; -// manage the masternode connections -void ProcessMasternodeConnections(){ - LOCK(cs_vNodes); - - BOOST_FOREACH(CNode* pnode, vNodes) - { - //if it's our masternode, let it be - if(darkSendPool.submittedToMasternode == pnode->addr) continue; - - if(pnode->fDarkSendMaster){ - LogPrintf("Closing masternode connection %s \n", pnode->addr.ToString().c_str()); - pnode->CloseSocketDisconnect(); - } - } -} - -void ProcessMessageMasternode(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +void ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { - - if (strCommand == "dsee") { //DarkSend Election Entry - if(fLiteMode) return; //disable all darksend/masternode related functionality - - bool fIsInitialDownload = IsInitialBlockDownload(); - if(fIsInitialDownload) return; - - CTxIn vin; - CService addr; - CPubKey pubkey; - CPubKey pubkey2; - vector vchSig; - int64_t sigTime; - int count; - int current; - int64_t lastUpdated; - int protocolVersion; - std::string strMessage; - - // 70047 and greater - vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion; - - // make sure signature isn't in the future (past is OK) - if (sigTime > GetAdjustedTime() + 60 * 60) { - LogPrintf("dsee - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); - return; - } - - bool isLocal = addr.IsRFC1918() || addr.IsLocal(); - if(RegTest()) isLocal = false; - - std::string vchPubKey(pubkey.begin(), pubkey.end()); - std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); - - strMessage = addr.ToString() + boost::lexical_cast(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(protocolVersion); - - if(protocolVersion < nMasternodeMinProtocol) { - LogPrintf("dsee - ignoring outdated masternode %s protocol version %d\n", vin.ToString().c_str(), protocolVersion); - return; - } - - CScript pubkeyScript; - pubkeyScript.SetDestination(pubkey.GetID()); - - if(pubkeyScript.size() != 25) { - LogPrintf("dsee - pubkey the wrong size\n"); - Misbehaving(pfrom->GetId(), 100); - return; - } - - CScript pubkeyScript2; - pubkeyScript2.SetDestination(pubkey2.GetID()); - - if(pubkeyScript2.size() != 25) { - LogPrintf("dsee - pubkey2 the wrong size\n"); - Misbehaving(pfrom->GetId(), 100); - return; - } - - std::string errorMessage = ""; - if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)){ - LogPrintf("dsee - Got bad masternode address signature\n"); - Misbehaving(pfrom->GetId(), 100); - return; - } - - if(Params().NetworkID() == CChainParams::MAIN){ - if(addr.GetPort() != 9999) return; - } - - //search existing masternode list, this is where we update existing masternodes with new dsee broadcasts - - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { - if(mn.vin.prevout == vin.prevout) { - // count == -1 when it's a new entry - // e.g. We don't want the entry relayed/time updated when we're syncing the list - // mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below, - // after that they just need to match - if(count == -1 && mn.pubkey == pubkey && !mn.UpdatedWithin(MASTERNODE_MIN_DSEE_SECONDS)){ - mn.UpdateLastSeen(); - - if(mn.now < sigTime){ //take the newest entry - LogPrintf("dsee - Got updated entry for %s\n", addr.ToString().c_str()); - mn.pubkey2 = pubkey2; - mn.now = sigTime; - mn.sig = vchSig; - mn.protocolVersion = protocolVersion; - mn.addr = addr; - - RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); - } - } - - return; - } - } - - // make sure the vout that was signed is related to the transaction that spawned the masternode - // - this is expensive, so it's only done once per masternode - if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubkey)) { - LogPrintf("dsee - Got mismatched pubkey and vin\n"); - Misbehaving(pfrom->GetId(), 100); - return; - } - - if(fDebug) LogPrintf("dsee - Got NEW masternode entry %s\n", addr.ToString().c_str()); - - // make sure it's still unspent - // - this is checked later by .check() in many places and by ThreadCheckDarkSendPool() - - CValidationState state; - CTransaction tx = CTransaction(); - CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey); - tx.vin.push_back(vin); - tx.vout.push_back(vout); - if(AcceptableInputs(mempool, state, tx)){ - if(fDebug) LogPrintf("dsee - Accepted masternode entry %i %i\n", count, current); - - if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ - LogPrintf("dsee - Input must have least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); - Misbehaving(pfrom->GetId(), 20); - return; - } - - // use this as a peer - addrman.Add(CAddress(addr), pfrom->addr, 2*60*60); - - // add our masternode - CMasterNode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2, protocolVersion); - mn.UpdateLastSeen(lastUpdated); - vecMasternodes.push_back(mn); - - // if it matches our masternodeprivkey, then we've been remotely activated - if(pubkey2 == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION){ - activeMasternode.EnableHotColdMasterNode(vin, addr); - } - - if(count == -1 && !isLocal) - RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); - - } else { - LogPrintf("dsee - Rejected masternode entry %s\n", addr.ToString().c_str()); - - int nDoS = 0; - if (state.IsInvalid(nDoS)) - { - LogPrintf("dsee - %s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), - pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); - if (nDoS > 0) - Misbehaving(pfrom->GetId(), nDoS); - } - } - } - - else if (strCommand == "dseep") { //DarkSend Election Entry Ping - if(fLiteMode) return; //disable all darksend/masternode related functionality - bool fIsInitialDownload = IsInitialBlockDownload(); - if(fIsInitialDownload) return; - - CTxIn vin; - vector vchSig; - int64_t sigTime; - bool stop; - vRecv >> vin >> vchSig >> sigTime >> stop; - - //LogPrintf("dseep - Received: vin: %s sigTime: %lld stop: %s\n", vin.ToString().c_str(), sigTime, stop ? "true" : "false"); - - if (sigTime > GetAdjustedTime() + 60 * 60) { - LogPrintf("dseep - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); - return; - } - - if (sigTime <= GetAdjustedTime() - 60 * 60) { - LogPrintf("dseep - Signature rejected, too far into the past %s - %d %d \n", vin.ToString().c_str(), sigTime, GetAdjustedTime()); - return; - } - - // see if we have this masternode - - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { - if(mn.vin.prevout == vin.prevout) { - // LogPrintf("dseep - Found corresponding mn for vin: %s\n", vin.ToString().c_str()); - // take this only if it's newer - if(mn.lastDseep < sigTime){ - std::string strMessage = mn.addr.ToString() + boost::lexical_cast(sigTime) + boost::lexical_cast(stop); - - std::string errorMessage = ""; - if(!darkSendSigner.VerifyMessage(mn.pubkey2, vchSig, strMessage, errorMessage)){ - LogPrintf("dseep - Got bad masternode address signature %s \n", vin.ToString().c_str()); - //Misbehaving(pfrom->GetId(), 100); - return; - } - - mn.lastDseep = sigTime; - - if(!mn.UpdatedWithin(MASTERNODE_MIN_DSEEP_SECONDS)){ - mn.UpdateLastSeen(); - if(stop) { - mn.Disable(); - mn.Check(); - } - RelayDarkSendElectionEntryPing(vin, vchSig, sigTime, stop); - } - } - return; - } - } - - if(fDebug) LogPrintf("dseep - Couldn't find masternode entry %s\n", vin.ToString().c_str()); - - std::map::iterator i = askedForMasternodeListEntry.find(vin.prevout); - if (i != askedForMasternodeListEntry.end()){ - int64_t t = (*i).second; - if (GetTime() < t) { - // we've asked recently - return; - } - } - - // ask for the dsee info once from the node that sent dseep - - LogPrintf("dseep - Asking source node for missing entry %s\n", vin.ToString().c_str()); - pfrom->PushMessage("dseg", vin); - int64_t askAgain = GetTime()+(60*60*24); - askedForMasternodeListEntry[vin.prevout] = askAgain; - - } else if (strCommand == "dseg") { //Get masternode list or specific entry - if(fLiteMode) return; //disable all darksend/masternode related functionality - CTxIn vin; - vRecv >> vin; - - if(vin == CTxIn()) { //only should ask for this once - //local network - if(!pfrom->addr.IsRFC1918() && Params().NetworkID() == CChainParams::MAIN) - { - std::map::iterator i = askedForMasternodeList.find(pfrom->addr); - if (i != askedForMasternodeList.end()) - { - int64_t t = (*i).second; - if (GetTime() < t) { - Misbehaving(pfrom->GetId(), 34); - LogPrintf("dseg - peer already asked me for the list\n"); - return; - } - } - - int64_t askAgain = GetTime()+(60*60*3); - askedForMasternodeList[pfrom->addr] = askAgain; - } - } //else, asking for a specific node which is ok - - int count = vecMasternodes.size(); - int i = 0; - - BOOST_FOREACH(CMasterNode mn, vecMasternodes) { - - if(mn.addr.IsRFC1918()) continue; //local network - - if(vin == CTxIn()){ - mn.Check(); - if(mn.IsEnabled()) { - if(fDebug) LogPrintf("dseg - Sending masternode entry - %s \n", mn.addr.ToString().c_str()); - pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion); - } - } else if (vin == mn.vin) { - if(fDebug) LogPrintf("dseg - Sending masternode entry - %s \n", mn.addr.ToString().c_str()); - pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion); - LogPrintf("dseg - Sent 1 masternode entries to %s\n", pfrom->addr.ToString().c_str()); - return; - } - i++; - } - - LogPrintf("dseg - Sent %d masternode entries to %s\n", count, pfrom->addr.ToString().c_str()); - } - - else if (strCommand == "mnget") { //Masternode Payments Request Sync + if (strCommand == "mnget") { //Masternode Payments Request Sync if(fLiteMode) return; //disable all darksend/masternode related functionality if(pfrom->HasFulfilledRequest("mnget")) { @@ -378,138 +84,6 @@ struct CompareValueOnly } }; -struct CompareValueOnly2 -{ - bool operator()(const pair& t1, - const pair& t2) const - { - return t1.first < t2.first; - } -}; - -int CountMasternodesAboveProtocol(int protocolVersion) -{ - int i = 0; - - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { - if(mn.protocolVersion < protocolVersion) continue; - i++; - } - - return i; - -} - - -int GetMasternodeByVin(CTxIn& vin) -{ - int i = 0; - - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { - if (mn.vin == vin) return i; - i++; - } - - return -1; -} - -int GetCurrentMasterNode(int mod, int64_t nBlockHeight, int minProtocol) -{ - int i = 0; - unsigned int score = 0; - int winner = -1; - - // scan for winner - BOOST_FOREACH(CMasterNode mn, vecMasternodes) { - mn.Check(); - if(mn.protocolVersion < minProtocol) continue; - if(!mn.IsEnabled()) { - i++; - continue; - } - - // calculate the score for each masternode - uint256 n = mn.CalculateScore(mod, nBlockHeight); - unsigned int n2 = 0; - memcpy(&n2, &n, sizeof(n2)); - - // determine the winner - if(n2 > score){ - score = n2; - winner = i; - } - i++; - } - - return winner; -} - -int GetMasternodeByRank(int findRank, int64_t nBlockHeight, int minProtocol) -{ - int i = 0; - - std::vector > vecMasternodeScores; - - i = 0; - BOOST_FOREACH(CMasterNode mn, vecMasternodes) { - mn.Check(); - if(mn.protocolVersion < minProtocol) continue; - if(!mn.IsEnabled()) { - i++; - continue; - } - - uint256 n = mn.CalculateScore(1, nBlockHeight); - unsigned int n2 = 0; - memcpy(&n2, &n, sizeof(n2)); - - vecMasternodeScores.push_back(make_pair(n2, i)); - i++; - } - - sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly2()); - - int rank = 0; - BOOST_FOREACH (PAIRTYPE(unsigned int, int)& s, vecMasternodeScores){ - rank++; - if(rank == findRank) return s.second; - } - - return -1; -} - -int GetMasternodeRank(CTxIn& vin, int64_t nBlockHeight, int minProtocol) -{ - std::vector > vecMasternodeScores; - - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { - mn.Check(); - - if(mn.protocolVersion < minProtocol) continue; - if(!mn.IsEnabled()) { - continue; - } - - uint256 n = mn.CalculateScore(1, nBlockHeight); - unsigned int n2 = 0; - memcpy(&n2, &n, sizeof(n2)); - - vecMasternodeScores.push_back(make_pair(n2, mn.vin)); - } - - sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly()); - - unsigned int rank = 0; - BOOST_FOREACH (PAIRTYPE(unsigned int, CTxIn)& s, vecMasternodeScores){ - rank++; - if(s.second == vin) { - return rank; - } - } - - return -1; -} - //Get the last hash that matches the modulus given. Processed in reverse order bool GetBlockHash(uint256& hash, int nBlockHeight) { @@ -548,12 +122,72 @@ bool GetBlockHash(uint256& hash, int nBlockHeight) return false; } +CMasternode::CMasternode() +{ + LOCK(cs); + vin = CTxIn(); + addr = CService(); + pubkey = CPubKey(); + pubkey2 = CPubKey(); + sig = std::vector(); + activeState = MASTERNODE_ENABLED; + now = GetTime(); + lastDseep = 0; + lastTimeSeen = 0; + cacheInputAge = 0; + cacheInputAgeBlock = 0; + unitTest = false; + allowFreeTx = true; + protocolVersion = MIN_PEER_PROTO_VERSION; + nLastDsq = 0; +} + +CMasternode::CMasternode(const CMasternode& other) +{ + LOCK(cs); + vin = other.vin; + addr = other.addr; + pubkey = other.pubkey; + pubkey2 = other.pubkey2; + sig = other.sig; + activeState = other.activeState; + now = other.now; + lastDseep = other.lastDseep; + lastTimeSeen = other.lastTimeSeen; + cacheInputAge = other.cacheInputAge; + cacheInputAgeBlock = other.cacheInputAgeBlock; + unitTest = other.unitTest; + allowFreeTx = other.allowFreeTx; + protocolVersion = other.protocolVersion; + nLastDsq = other.nLastDsq; +} + +CMasternode::CMasternode(CService newAddr, CTxIn newVin, CPubKey newPubkey, std::vector newSig, int64_t newNow, CPubKey newPubkey2, int protocolVersionIn) +{ + LOCK(cs); + vin = newVin; + addr = newAddr; + pubkey = newPubkey; + pubkey2 = newPubkey2; + sig = newSig; + activeState = MASTERNODE_ENABLED; + now = newNow; + lastDseep = 0; + lastTimeSeen = 0; + cacheInputAge = 0; + cacheInputAgeBlock = 0; + unitTest = false; + allowFreeTx = true; + protocolVersion = protocolVersionIn; + nLastDsq = 0; +} + // // Deterministically calculate a given "score" for a masternode depending on how close it's hash is to // the proof of work for that block. The further away they are the better, the furthest will win the election // and get paid this block // -uint256 CMasterNode::CalculateScore(int mod, int64_t nBlockHeight) +uint256 CMasternode::CalculateScore(int mod, int64_t nBlockHeight) { if(chainActive.Tip() == NULL) return 0; @@ -570,19 +204,19 @@ uint256 CMasterNode::CalculateScore(int mod, int64_t nBlockHeight) return r; } -void CMasterNode::Check() +void CMasternode::Check() { //once spent, stop doing the checks - if(enabled==3) return; + if(activeState == MASTERNODE_VIN_SPENT) return; if(!UpdatedWithin(MASTERNODE_REMOVAL_SECONDS)){ - enabled = 4; + activeState = MASTERNODE_REMOVE; return; } if(!UpdatedWithin(MASTERNODE_EXPIRATION_SECONDS)){ - enabled = 2; + activeState = MASTERNODE_EXPIRED; return; } @@ -594,12 +228,12 @@ void CMasterNode::Check() tx.vout.push_back(vout); if(!AcceptableInputs(mempool, state, tx)){ - enabled = 3; + activeState = MASTERNODE_VIN_SPENT; return; } } - enabled = 1; // OK + activeState = MASTERNODE_ENABLED; // OK } bool CMasternodePayments::CheckSignature(CMasternodePaymentWinner& winner) @@ -721,7 +355,7 @@ void CMasternodePayments::CleanPaymentList() { if(chainActive.Tip() == NULL) return; - int nLimit = std::max(((int)vecMasternodes.size())*2, 1000); + int nLimit = std::max(((int)mnodeman.size())*2, 1000); vector::iterator it; for(it=vWinning.begin();itvin; + newWinner.payee.SetDestination(pmn->pubkey.GetID()); } //if we can't find new MN to get paid, pick first active MN counting back from the end of vecLastPayments list - if(newWinner.nBlockHeight == 0 && vecMasternodes.size() > 1) + if(newWinner.nBlockHeight == 0 && mnodeman.CountEnabled() > 0) { - BOOST_REVERSE_FOREACH(CTxIn& vin, vecLastPayments) + BOOST_REVERSE_FOREACH(CTxIn& vinLP, vecLastPayments) { - BOOST_FOREACH(CMasterNode& mn, vecMasternodes) - if(mn.vin == vin) - { - mn.Check(); - if(!mn.IsEnabled()) break; - - newWinner.score = 0; - newWinner.nBlockHeight = nBlockHeight; - newWinner.vin = vin; - newWinner.payee.SetDestination(mn.pubkey.GetID()); - break; - } - - if(newWinner.nBlockHeight != 0) break; // we found active MN + CMasternode* pmn = mnodeman.Find(vinLP); + if(pmn != NULL) + { + pmn->Check(); + if(!pmn->IsEnabled()) continue; + + newWinner.score = 0; + newWinner.nBlockHeight = nBlockHeight; + newWinner.vin = pmn->vin; + newWinner.payee.SetDestination(pmn->pubkey.GetID()); + break; // we found active MN + } } } + if(newWinner.nBlockHeight == 0) return false; + if(Sign(newWinner)) { if(AddWinningMasternode(newWinner)) diff --git a/src/masternode.h b/src/masternode.h index 49cf49b609923..d23628c971680 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -16,9 +16,6 @@ #include "base58.h" #include "main.h" -class CMasterNode; -class CMasternodePayments; - #define MASTERNODE_NOT_PROCESSED 0 // initial state #define MASTERNODE_IS_CAPABLE 1 #define MASTERNODE_NOT_CAPABLE 2 @@ -38,74 +35,121 @@ class CMasternodePayments; using namespace std; +class CMasternode; +class CMasternodePayments; class CMasternodePaymentWinner; -extern std::vector vecMasternodes; extern CMasternodePayments masternodePayments; -extern std::vector vecMasternodeAskedFor; extern map mapSeenMasternodeVotes; extern map mapCacheBlockHashes; +enum masternodeState { + MASTERNODE_ENABLED = 1, + MASTERNODE_EXPIRED = 2, + MASTERNODE_VIN_SPENT = 3, + MASTERNODE_REMOVE = 4 +}; -// manage the masternode connections -void ProcessMasternodeConnections(); -int CountMasternodesAboveProtocol(int protocolVersion); - -// Get the current winner for this block -int GetCurrentMasterNode(int mod=1, int64_t nBlockHeight=0, int minProtocol=0); - -int GetMasternodeByVin(CTxIn& vin); -int GetMasternodeRank(CTxIn& vin, int64_t nBlockHeight=0, int minProtocol=0); -int GetMasternodeByRank(int findRank, int64_t nBlockHeight=0, int minProtocol=0); - -void ProcessMessageMasternode(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); +void 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 // it's the one who own that ip address and code for calculating the payment election. // -class CMasterNode +class CMasternode { +private: + // critical section to protect the inner data structures + mutable CCriticalSection cs; + public: - CService addr; CTxIn vin; - int64_t lastTimeSeen; + CService addr; CPubKey pubkey; CPubKey pubkey2; std::vector sig; + int activeState; int64_t now; //dsee message times int64_t lastDseep; + int64_t lastTimeSeen; int cacheInputAge; int cacheInputAgeBlock; - int enabled; bool unitTest; bool allowFreeTx; int protocolVersion; + int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node - //the dsq count from the last dsq broadcast of this node - int64_t nLastDsq; + CMasternode(); + CMasternode(const CMasternode& other); + CMasternode(CService newAddr, CTxIn newVin, CPubKey newPubkey, std::vector newSig, int64_t newNow, CPubKey newPubkey2, int protocolVersionIn); - CMasterNode(CService newAddr, CTxIn newVin, CPubKey newPubkey, std::vector newSig, int64_t newNow, CPubKey newPubkey2, int protocolVersionIn) + void swap(CMasternode& first, CMasternode& second) // nothrow { - addr = newAddr; - vin = newVin; - pubkey = newPubkey; - pubkey2 = newPubkey2; - sig = newSig; - now = newNow; - enabled = 1; - lastTimeSeen = 0; - unitTest = false; - cacheInputAge = 0; - cacheInputAgeBlock = 0; - nLastDsq = 0; - lastDseep = 0; - allowFreeTx = true; - protocolVersion = protocolVersionIn; + // enable ADL (not necessary in our case, but good practice) + using std::swap; + + // by swapping the members of two classes, + // the two classes are effectively swapped + swap(first.vin, second.vin); + swap(first.addr, second.addr); + swap(first.pubkey, second.pubkey); + swap(first.pubkey2, second.pubkey2); + swap(first.sig, second.sig); + swap(first.activeState, second.activeState); + swap(first.now, second.now); + swap(first.lastDseep, second.lastDseep); + swap(first.lastTimeSeen, second.lastTimeSeen); + swap(first.cacheInputAge, second.cacheInputAge); + swap(first.cacheInputAgeBlock, second.cacheInputAgeBlock); + swap(first.allowFreeTx, second.allowFreeTx); + swap(first.protocolVersion, second.protocolVersion); + swap(first.unitTest, second.unitTest); + swap(first.nLastDsq, second.nLastDsq); + } + + CMasternode& operator=(CMasternode from) + { + swap(*this, from); + return *this; + } + friend bool operator==(const CMasternode& a, const CMasternode& b) + { + return a.vin == b.vin; + } + friend bool operator!=(const CMasternode& a, const CMasternode& b) + { + return !(a.vin == b.vin); } uint256 CalculateScore(int mod=1, int64_t nBlockHeight=0); + IMPLEMENT_SERIALIZE + ( + // serialized format: + // * version byte (currently 0) + // * all fields (?) + { + LOCK(cs); + unsigned char nVersion = 0; + READWRITE(nVersion); + READWRITE(vin); + READWRITE(addr); + READWRITE(pubkey); + READWRITE(pubkey2); + READWRITE(sig); + READWRITE(activeState); + READWRITE(now); + READWRITE(lastDseep); + READWRITE(lastTimeSeen); + READWRITE(cacheInputAge); + READWRITE(cacheInputAgeBlock); + READWRITE(unitTest); + READWRITE(allowFreeTx); + READWRITE(protocolVersion); + READWRITE(nLastDsq); + } + ) + void UpdateLastSeen(int64_t override=0) { if(override == 0){ @@ -138,7 +182,7 @@ class CMasterNode bool IsEnabled() { - return enabled == 1; + return activeState == MASTERNODE_ENABLED; } int GetMasternodeInputAge() @@ -154,7 +198,6 @@ class CMasterNode } }; - // for storing the winning payments class CMasternodePaymentWinner { @@ -227,7 +270,7 @@ class CMasternodePayments void Relay(CMasternodePaymentWinner& winner); void Sync(CNode* node); void CleanPaymentList(); - int LastPayment(CMasterNode& mn); + int LastPayment(CMasternode& mn); //slow bool GetBlockPayee(int nBlockHeight, CScript& payee); @@ -276,5 +319,4 @@ class CMasternodePayments bool GetBlockPayee(int nBlockHeight, CScript& payee); };*/ - #endif diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp new file mode 100644 index 0000000000000..ec05c7c95cfbf --- /dev/null +++ b/src/masternodeman.cpp @@ -0,0 +1,622 @@ +// Copyright (c) 2014-2015 The Darkcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "masternodeman.h" +#include "activemasternode.h" +#include "darksend.h" +#include "core.h" +#include "util.h" +#include "addrman.h" +#include +#include + + +/** Masternode manager */ +CMasternodeMan mnodeman; + +struct CompareValueOnly +{ + bool operator()(const pair& t1, + const pair& t2) const + { + return t1.first < t2.first; + } +}; + +// +// CMasternodeDB +// + +CMasternodeDB::CMasternodeDB() +{ + pathMN = GetDataDir() / "masternodes.dat"; +} + +bool CMasternodeDB::Write(const CMasternodeMan& mnodemanToSave) +{ + // serialize addresses, checksum data up to that point, then append csum + CDataStream ssMasternodes(SER_DISK, CLIENT_VERSION); + ssMasternodes << FLATDATA(Params().MessageStart()); + ssMasternodes << mnodemanToSave; + uint256 hash = Hash(ssMasternodes.begin(), ssMasternodes.end()); + ssMasternodes << hash; + + // open output file, and associate with CAutoFile + FILE *file = fopen(pathMN.string().c_str(), "wb"); + CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("%s : Failed to open file %s", __func__, pathMN.string()); + + // Write and commit header, data + try { + fileout << ssMasternodes; + } + catch (std::exception &e) { + return error("%s : Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout); + fileout.fclose(); + + return true; +} + +bool CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathMN.string().c_str(), "rb"); + CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!filein) + return error("%s : Failed to open file %s", __func__, pathMN.string()); + + // use file size to size memory buffer + int fileSize = boost::filesystem::file_size(pathMN); + int dataSize = fileSize - sizeof(uint256); + // Don't try to resize to a negative number if file is small + if (dataSize < 0) + dataSize = 0; + vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (std::exception &e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + } + filein.fclose(); + + CDataStream ssMasternodes(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssMasternodes.begin(), ssMasternodes.end()); + if (hashIn != hashTmp) + return error("%s : Checksum mismatch, data corrupted", __func__); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssMasternodes >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("%s : Invalid network magic number", __func__); + + // de-serialize address data into one CMnList object + ssMasternodes >> mnodemanToLoad; + } + catch (std::exception &e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + mnodemanToLoad.Clear(); + } + + return true; +} + +void DumpMasternodes() +{ + int64_t nStart = GetTimeMillis(); + + CMasternodeDB mndb; + mndb.Write(mnodeman); + + LogPrint("masternode", "Flushed %d masternodes to masternodes.dat %dms\n", + mnodeman.size(), GetTimeMillis() - nStart); +} + +CMasternodeMan::CMasternodeMan() {} + +bool CMasternodeMan::Add(CMasternode &mn) +{ + LOCK(cs); + + if (!mn.IsEnabled()) + return false; + + CMasternode *pmn = Find(mn.vin); + + if (pmn == NULL) + { + vMasternodes.push_back(mn); + return true; + } + + return false; +} + +void CMasternodeMan::Check() +{ + LOCK(cs); + + BOOST_FOREACH(CMasternode& mn, vMasternodes) + mn.Check(); +} + +void CMasternodeMan::CheckAndRemove() +{ + LOCK(cs); + + Check(); + + //remove inactive + vector::iterator it = vMasternodes.begin(); + while(it != vMasternodes.end()){ + if((*it).activeState == 4 || (*it).activeState == 3){ + LogPrintf("Removing inactive masternode %s\n", (*it).addr.ToString().c_str()); + it = vMasternodes.erase(it); + } else { + ++it; + } + } + + // check who's asked for the masternode list + map::iterator it1 = mAskedUsForMasternodeList.begin(); + while(it1 != mAskedUsForMasternodeList.end()){ + if((*it1).second < GetTime()) + it1 = mAskedUsForMasternodeList.erase(it1); + else ++it1; + } + + // check who we asked for the masternode list + it1 = mWeAskedForMasternodeList.begin(); + while(it1 != mWeAskedForMasternodeList.end()){ + if((*it1).second < GetTime()) + it1 = mWeAskedForMasternodeList.erase(it1); + else ++it1; + } + + // check which masternodes we've asked for + map::iterator it2 = mWeAskedForMasternodeListEntry.begin(); + while(it2 != mWeAskedForMasternodeListEntry.end()){ + if((*it2).second < GetTime()) + it2 = mWeAskedForMasternodeListEntry.erase(it2); + else ++it2; + } + +} + +int CMasternodeMan::CountEnabled() +{ + int i = 0; + + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + mn.Check(); + if(mn.IsEnabled()) i++; + } + + return i; +} + +int CMasternodeMan::CountMasternodesAboveProtocol(int protocolVersion) +{ + int i = 0; + + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + mn.Check(); + if(mn.protocolVersion < protocolVersion || !mn.IsEnabled()) continue; + i++; + } + + return i; +} + +void CMasternodeMan::DsegUpdate(CNode* pnode) +{ + std::map::iterator it = mWeAskedForMasternodeList.find(pnode->addr); + if (it != mWeAskedForMasternodeList.end()) + { + if (GetTime() < (*it).second) { + LogPrintf("dseg - we already asked %s for the list; skipping...\n", pnode->addr.ToString()); + return; + } + } + pnode->PushMessage("dseg", CTxIn()); + int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; + mWeAskedForMasternodeList[pnode->addr] = askAgain; +} + +CMasternode *CMasternodeMan::Find(const CTxIn &vin) +{ + LOCK(cs); + + BOOST_FOREACH(CMasternode& mn, vMasternodes) + { + if(mn.vin == vin) + return &mn; + } + return NULL; +} + +CMasternode *CMasternodeMan::FindNotInVec(const std::vector &vVins) +{ + LOCK(cs); + + BOOST_FOREACH(CMasternode &mn, vMasternodes) + { + mn.Check(); + if(!mn.IsEnabled()) continue; + + bool found = false; + BOOST_FOREACH(const CTxIn& vin, vVins) + if(mn.vin == vin) + { + found = true; + break; + } + + if(found) continue; + + return &mn; + } + + return NULL; +} + +CMasternode *CMasternodeMan::FindRandom() +{ + LOCK(cs); + + if(size() == 0) return NULL; + + return &vMasternodes[GetRandInt(vMasternodes.size())]; +} + +CMasternode* CMasternodeMan::GetCurrentMasterNode(int mod, int64_t nBlockHeight, int minProtocol) +{ + unsigned int score = 0; + CMasternode* winner = NULL; + + // scan for winner + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + mn.Check(); + if(mn.protocolVersion < minProtocol || !mn.IsEnabled()) continue; + + // calculate the score for each masternode + uint256 n = mn.CalculateScore(mod, nBlockHeight); + unsigned int n2 = 0; + memcpy(&n2, &n, sizeof(n2)); + + // determine the winner + if(n2 > score){ + score = n2; + winner = &mn; + } + } + + return winner; +} + +int CMasternodeMan::GetMasternodeRank(const CTxIn& vin, int64_t nBlockHeight, int minProtocol) +{ + std::vector > vecMasternodeScores; + + // scan for winner + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + + mn.Check(); + + if(mn.protocolVersion < minProtocol) continue; + if(!mn.IsEnabled()) { + continue; + } + + uint256 n = mn.CalculateScore(1, nBlockHeight); + unsigned int n2 = 0; + memcpy(&n2, &n, sizeof(n2)); + + vecMasternodeScores.push_back(make_pair(n2, mn.vin)); + } + + sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly()); + + unsigned int rank = 0; + BOOST_FOREACH (PAIRTYPE(unsigned int, CTxIn)& s, vecMasternodeScores){ + rank++; + if(s.second == vin) { + return rank; + } + } + + return -1; +} + +void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + + if(fLiteMode) return; //disable all darksend/masternode related functionality + if(IsInitialBlockDownload()) return; + + LOCK(cs); + + if (strCommand == "dsee") { //DarkSend Election Entry + + CTxIn vin; + CService addr; + CPubKey pubkey; + CPubKey pubkey2; + vector vchSig; + int64_t sigTime; + int count; + int current; + int64_t lastUpdated; + int protocolVersion; + std::string strMessage; + + // 70047 and greater + vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion; + + // make sure signature isn't in the future (past is OK) + if (sigTime > GetAdjustedTime() + 60 * 60) { + LogPrintf("dsee - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); + return; + } + + bool isLocal = addr.IsRFC1918() || addr.IsLocal(); + if(RegTest()) isLocal = false; + + std::string vchPubKey(pubkey.begin(), pubkey.end()); + std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); + + strMessage = addr.ToString() + boost::lexical_cast(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(protocolVersion); + + if(protocolVersion < nMasternodeMinProtocol) { + LogPrintf("dsee - ignoring outdated masternode %s protocol version %d\n", vin.ToString().c_str(), protocolVersion); + return; + } + + CScript pubkeyScript; + pubkeyScript.SetDestination(pubkey.GetID()); + + if(pubkeyScript.size() != 25) { + LogPrintf("dsee - pubkey the wrong size\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + CScript pubkeyScript2; + pubkeyScript2.SetDestination(pubkey2.GetID()); + + if(pubkeyScript2.size() != 25) { + LogPrintf("dsee - pubkey2 the wrong size\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)){ + LogPrintf("dsee - Got bad masternode address signature\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + if(Params().NetworkID() == CChainParams::MAIN){ + if(addr.GetPort() != 9999) return; + } + + //search existing masternode list, this is where we update existing masternodes with new dsee broadcasts + CMasternode* pmn = this->Find(vin); + if(pmn != NULL) + { + // count == -1 when it's a new entry + // e.g. We don't want the entry relayed/time updated when we're syncing the list + // mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below, + // after that they just need to match + if(count == -1 && pmn->pubkey == pubkey && !pmn->UpdatedWithin(MASTERNODE_MIN_DSEE_SECONDS)){ + pmn->UpdateLastSeen(); + + if(pmn->now < sigTime){ //take the newest entry + LogPrintf("dsee - Got updated entry for %s\n", addr.ToString().c_str()); + pmn->pubkey2 = pubkey2; + pmn->now = sigTime; + pmn->sig = vchSig; + pmn->protocolVersion = protocolVersion; + pmn->addr = addr; + pmn->Check(); + if(pmn->IsEnabled()) + RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); + } + } + + return; + } + + // make sure the vout that was signed is related to the transaction that spawned the masternode + // - this is expensive, so it's only done once per masternode + if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubkey)) { + LogPrintf("dsee - Got mismatched pubkey and vin\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + if(fDebug) LogPrintf("dsee - Got NEW masternode entry %s\n", addr.ToString().c_str()); + + // make sure it's still unspent + // - this is checked later by .check() in many places and by ThreadCheckDarkSendPool() + + CValidationState state; + CTransaction tx = CTransaction(); + CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey); + tx.vin.push_back(vin); + tx.vout.push_back(vout); + if(AcceptableInputs(mempool, state, tx)){ + if(fDebug) LogPrintf("dsee - Accepted masternode entry %i %i\n", count, current); + + if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ + LogPrintf("dsee - Input must have least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); + Misbehaving(pfrom->GetId(), 20); + return; + } + + // use this as a peer + addrman.Add(CAddress(addr), pfrom->addr, 2*60*60); + + // add our masternode + CMasternode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2, protocolVersion); + mn.UpdateLastSeen(lastUpdated); + this->Add(mn); + + // if it matches our masternodeprivkey, then we've been remotely activated + if(pubkey2 == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION){ + activeMasternode.EnableHotColdMasterNode(vin, addr); + } + + if(count == -1 && !isLocal) + RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); + + } else { + LogPrintf("dsee - Rejected masternode entry %s\n", addr.ToString().c_str()); + + int nDoS = 0; + if (state.IsInvalid(nDoS)) + { + LogPrintf("dsee - %s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); + if (nDoS > 0) + Misbehaving(pfrom->GetId(), nDoS); + } + } + } + + else if (strCommand == "dseep") { //DarkSend Election Entry Ping + + CTxIn vin; + vector vchSig; + int64_t sigTime; + bool stop; + vRecv >> vin >> vchSig >> sigTime >> stop; + + //LogPrintf("dseep - Received: vin: %s sigTime: %lld stop: %s\n", vin.ToString().c_str(), sigTime, stop ? "true" : "false"); + + if (sigTime > GetAdjustedTime() + 60 * 60) { + LogPrintf("dseep - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); + return; + } + + if (sigTime <= GetAdjustedTime() - 60 * 60) { + LogPrintf("dseep - Signature rejected, too far into the past %s - %d %d \n", vin.ToString().c_str(), sigTime, GetAdjustedTime()); + return; + } + + // see if we have this masternode + CMasternode* pmn = this->Find(vin); + if(pmn != NULL) + { + // LogPrintf("dseep - Found corresponding mn for vin: %s\n", vin.ToString().c_str()); + // take this only if it's newer + if(pmn->lastDseep < sigTime) + { + std::string strMessage = pmn->addr.ToString() + boost::lexical_cast(sigTime) + boost::lexical_cast(stop); + + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) + { + LogPrintf("dseep - Got bad masternode address signature %s \n", vin.ToString().c_str()); + //Misbehaving(pfrom->GetId(), 100); + return; + } + + pmn->lastDseep = sigTime; + + if(!pmn->UpdatedWithin(MASTERNODE_MIN_DSEEP_SECONDS)) + { + if(stop) pmn->Disable(); + else + { + pmn->UpdateLastSeen(); + pmn->Check(); + if(!pmn->IsEnabled()) return; + } + RelayDarkSendElectionEntryPing(vin, vchSig, sigTime, stop); + } + } + return; + } + + if(fDebug) LogPrintf("dseep - Couldn't find masternode entry %s\n", vin.ToString().c_str()); + + std::map::iterator i = mWeAskedForMasternodeListEntry.find(vin.prevout); + if (i != mWeAskedForMasternodeListEntry.end()) + { + int64_t t = (*i).second; + if (GetTime() < t) return; // we've asked recently + } + + // ask for the dsee info once from the node that sent dseep + + LogPrintf("dseep - Asking source node for missing entry %s\n", vin.ToString().c_str()); + pfrom->PushMessage("dseg", vin); + int64_t askAgain = GetTime()+(60*60*24); + mWeAskedForMasternodeListEntry[vin.prevout] = askAgain; + + } else if (strCommand == "dseg") { //Get masternode list or specific entry + + CTxIn vin; + vRecv >> vin; + + if(vin == CTxIn()) { //only should ask for this once + //local network + if(!pfrom->addr.IsRFC1918() && Params().NetworkID() == CChainParams::MAIN) + { + std::map::iterator i = mAskedUsForMasternodeList.find(pfrom->addr); + if (i != mAskedUsForMasternodeList.end()) + { + int64_t t = (*i).second; + if (GetTime() < t) { + Misbehaving(pfrom->GetId(), 34); + LogPrintf("dseg - peer already asked me for the list\n"); + return; + } + } + int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; + mAskedUsForMasternodeList[pfrom->addr] = askAgain; + } + } //else, asking for a specific node which is ok + + int count = this->size(); + int i = 0; + + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + + if(mn.addr.IsRFC1918()) continue; //local network + + if(mn.IsEnabled()) + { + if(fDebug) LogPrintf("dseg - Sending masternode entry - %s \n", mn.addr.ToString().c_str()); + if(vin == CTxIn()){ + pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion); + } else if (vin == mn.vin) { + pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion); + LogPrintf("dseg - Sent 1 masternode entries to %s\n", pfrom->addr.ToString().c_str()); + return; + } + i++; + } + } + + LogPrintf("dseg - Sent %d masternode entries to %s\n", i, pfrom->addr.ToString().c_str()); + } + +} diff --git a/src/masternodeman.h b/src/masternodeman.h new file mode 100644 index 0000000000000..4cfc274c819bf --- /dev/null +++ b/src/masternodeman.h @@ -0,0 +1,118 @@ +// Copyright (c) 2014-2015 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 MASTERNODEMAN_H +#define MASTERNODEMAN_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" +#include "masternode.h" + +#define MASTERNODES_DUMP_SECONDS (15*60) +#define MASTERNODES_DSEG_SECONDS (3*60*60) + +using namespace std; + +class CMasternodeMan; + +extern CMasternodeMan mnodeman; +void DumpMasternodes(); + +/** Access to the MN database (masternodes.dat) */ +class CMasternodeDB +{ +private: + boost::filesystem::path pathMN; +public: + CMasternodeDB(); + bool Write(const CMasternodeMan &mnodemanToSave); + bool Read(CMasternodeMan& mnodemanToLoad); +}; + +class CMasternodeMan +{ +private: + // critical section to protect the inner data structures + mutable CCriticalSection cs; + + // map to hold all MNs + std::vector vMasternodes; + // who's asked for the masternode list and the last time + std::map mAskedUsForMasternodeList; + // who we asked for the masternode list and the last time + std::map mWeAskedForMasternodeList; + // which masternodes we've asked for + std::map mWeAskedForMasternodeListEntry; + +public: + + IMPLEMENT_SERIALIZE + ( + // serialized format: + // * version byte (currently 0) + // * masternodes vector + { + LOCK(cs); + unsigned char nVersion = 0; + READWRITE(nVersion); + READWRITE(vMasternodes); + READWRITE(mAskedUsForMasternodeList); + READWRITE(mWeAskedForMasternodeList); + READWRITE(mWeAskedForMasternodeListEntry); + } + ) + + CMasternodeMan(); + CMasternodeMan(CMasternodeMan& other); + + // Add an entry + + bool Add(CMasternode &mn); + + // Check all masternodes + void Check(); + + // Check all masternodes and remove inactive + void CheckAndRemove(); + + // Clear masternode vector + void Clear() { vMasternodes.clear(); } + + int CountEnabled(); + + int CountMasternodesAboveProtocol(int protocolVersion); + + void DsegUpdate(CNode* pnode); + + // Find an entry + CMasternode* Find(const CTxIn& vin); + + //Find an entry thta do not match every entry provided vector + CMasternode* FindNotInVec(const std::vector &vVins); + + // Find a random entry + CMasternode* FindRandom(); + + // Get the current winner for this block + CMasternode* GetCurrentMasterNode(int mod=1, int64_t nBlockHeight=0, int minProtocol=0); + + std::vector GetFullMasternodeVector() { Check(); return vMasternodes; } + + int GetMasternodeRank(const CTxIn &vin, int64_t nBlockHeight, int minProtocol=0); + + void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + + // Return the number of (unique) masternodes + int size() { return vMasternodes.size(); } + +}; + +#endif diff --git a/src/miner.cpp b/src/miner.cpp index 691e22d70557a..c770978852e74 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -12,7 +12,7 @@ #ifdef ENABLE_WALLET #include "wallet.h" #endif -#include "masternode.h" +#include "masternodeman.h" ////////////////////////////////////////////////////////////////////////////// // @@ -163,9 +163,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) //spork if(!masternodePayments.GetBlockPayee(pindexPrev->nHeight+1, pblock->payee)){ //no masternode detected - int winningNode = GetCurrentMasterNode(1); - if(winningNode >= 0){ - pblock->payee.SetDestination(vecMasternodes[winningNode].pubkey.GetID()); + CMasternode* winningNode = mnodeman.GetCurrentMasterNode(1); + if(winningNode){ + pblock->payee.SetDestination(winningNode->pubkey.GetID()); } else { LogPrintf("CreateNewBlock: Failed to detect masternode to pay\n"); hasPayment = false; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index c4294ae005c26..7fb2a7b78af16 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -13,6 +13,7 @@ #include "main.h" #include "net.h" #include "ui_interface.h" +#include "masternodeman.h" #include @@ -24,7 +25,7 @@ static const int64_t nClientStartupTime = GetTime(); ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : QObject(parent), optionsModel(optionsModel), - cachedNumBlocks(0), + cachedNumBlocks(0), cachedMasternodeCountString(""), cachedReindexing(0), cachedImporting(0), numBlocksAtStartup(-1), pollTimer(0) { @@ -32,6 +33,11 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); pollTimer->start(MODEL_UPDATE_DELAY); + pollMnTimer = new QTimer(this); + connect(pollMnTimer, SIGNAL(timeout()), this, SLOT(updateMnTimer())); + // no need to update as frequent as data for balances/txes/blocks + pollMnTimer->start(MODEL_UPDATE_DELAY * 4); + subscribeToCoreSignals(); } @@ -54,6 +60,11 @@ int ClientModel::getNumConnections(unsigned int flags) const return nNum; } +QString ClientModel::getMasternodeCountString() const +{ + return QString::number((int)mnodeman.CountEnabled()) + " / " + QString::number((int)mnodeman.size()); +} + int ClientModel::getNumBlocks() const { LOCK(cs_main); @@ -117,6 +128,24 @@ void ClientModel::updateTimer() emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); } +void ClientModel::updateMnTimer() +{ + // Get required lock upfront. This avoids the GUI from getting stuck on + // periodical polls if the core is holding the locks for a longer time - + // for example, during a wallet rescan. + TRY_LOCK(cs_main, lockMain); + if(!lockMain) + return; + QString newMasternodeCountString = getMasternodeCountString(); + + if (cachedMasternodeCountString != newMasternodeCountString) + { + cachedMasternodeCountString = newMasternodeCountString; + + emit strMasternodesChanged(cachedMasternodeCountString); + } +} + void ClientModel::updateNumConnections(int numConnections) { emit numConnectionsChanged(numConnections); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 6cb3fb73c1f38..ac079ff45bc7b 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -46,6 +46,7 @@ class ClientModel : public QObject //! Return number of connections, default is in- and outbound (total) int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; + QString getMasternodeCountString() const; int getNumBlocks() const; int getNumBlocksAtStartup(); @@ -74,12 +75,14 @@ class ClientModel : public QObject OptionsModel *optionsModel; int cachedNumBlocks; + QString cachedMasternodeCountString; bool cachedReindexing; bool cachedImporting; int numBlocksAtStartup; QTimer *pollTimer; + QTimer *pollMnTimer; void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); @@ -87,6 +90,7 @@ class ClientModel : public QObject signals: void numConnectionsChanged(int count); void numBlocksChanged(int count); + void strMasternodesChanged(const QString &strMasternodes); void alertsChanged(const QString &warnings); void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); @@ -95,6 +99,7 @@ class ClientModel : public QObject public slots: void updateTimer(); + void updateMnTimer(); void updateNumConnections(int numConnections); void updateAlert(const QString &hash, int status); }; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 9257f71bbbc70..5ad6d2b1b6a07 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -11,7 +11,6 @@ #include "rpcserver.h" #include "rpcclient.h" -#include "masternode.h" #include "json/json_spirit_value.h" #include @@ -276,6 +275,9 @@ void RPCConsole::setClientModel(ClientModel *model) setNumBlocks(model->getNumBlocks()); connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + setMasternodeCount(model->getMasternodeCountString()); + connect(model, SIGNAL(strMasternodesChanged(QString)), this, SLOT(setMasternodeCount(QString))); + updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); @@ -373,11 +375,11 @@ void RPCConsole::setNumBlocks(int count) ui->numberOfBlocks->setText(QString::number(count)); if(clientModel) ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString()); +} - // set masternode count - - QString masternodes = QString::number((int)vecMasternodes.size()); - ui->masternodeCount->setText(masternodes); +void RPCConsole::setMasternodeCount(const QString &strMasternodes) +{ + ui->masternodeCount->setText(strMasternodes); } void RPCConsole::on_lineEdit_returnPressed() diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index ba715156bda70..926497c34fe9d 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -53,6 +53,8 @@ public slots: void setNumConnections(int count); /** Set number of blocks shown in the UI */ void setNumBlocks(int count); + /** Set number of masternodes shown in the UI */ + void setMasternodeCount(const QString &strMasternodes); /** Go forward or back in history */ void browseHistory(int offset); /** Scroll console view to end */ diff --git a/src/rpcdarksend.cpp b/src/rpcdarksend.cpp index 01734e37da620..b4b1b668d50dc 100644 --- a/src/rpcdarksend.cpp +++ b/src/rpcdarksend.cpp @@ -7,8 +7,8 @@ #include "core.h" #include "db.h" #include "init.h" -#include "masternode.h" #include "activemasternode.h" +#include "masternodeman.h" #include "masternodeconfig.h" #include "rpcserver.h" #include @@ -77,7 +77,7 @@ Value getpoolinfo(const Array& params, bool fHelp) "Returns an object containing anonymous pool-related information."); Object obj; - obj.push_back(Pair("current_masternode", GetCurrentMasterNode())); + obj.push_back(Pair("current_masternode", mnodeman.GetCurrentMasterNode()->addr.ToString())); obj.push_back(Pair("state", darkSendPool.GetState())); obj.push_back(Pair("entries", darkSendPool.GetEntriesCount())); obj.push_back(Pair("entries_accepted", darkSendPool.GetCountEntriesAccepted())); @@ -239,46 +239,20 @@ Value masternode(const Array& params, bool fHelp) if (strCommand == "list") { - std::string strCommand = "active"; - - if (params.size() == 2){ - strCommand = params[1].get_str().c_str(); - } + Array newParams(params.size() - 1); + std::copy(params.begin() + 1, params.end(), newParams.begin()); + return masternodelist(newParams, fHelp); + } - if (strCommand != "active" && strCommand != "vin" && strCommand != "pubkey" && strCommand != "lastseen" && strCommand != "activeseconds" && strCommand != "rank" && strCommand != "protocol"){ + if (strCommand == "count") + { + if (params.size() > 2){ throw runtime_error( - "list supports 'active', 'vin', 'pubkey', 'lastseen', 'activeseconds', 'rank', 'protocol'\n"); + "too many parameters\n"); } - - Object obj; - BOOST_FOREACH(CMasterNode mn, vecMasternodes) { - mn.Check(); - - if(strCommand == "active"){ - obj.push_back(Pair(mn.addr.ToString().c_str(), (int)mn.IsEnabled())); - } else if (strCommand == "vin") { - obj.push_back(Pair(mn.addr.ToString().c_str(), mn.vin.prevout.hash.ToString().c_str())); - } else if (strCommand == "pubkey") { - CScript pubkey; - pubkey.SetDestination(mn.pubkey.GetID()); - CTxDestination address1; - ExtractDestination(pubkey, address1); - CBitcoinAddress address2(address1); - - obj.push_back(Pair(mn.addr.ToString().c_str(), address2.ToString().c_str())); - } else if (strCommand == "protocol") { - obj.push_back(Pair(mn.addr.ToString().c_str(), (int64_t)mn.protocolVersion)); - } else if (strCommand == "lastseen") { - obj.push_back(Pair(mn.addr.ToString().c_str(), (int64_t)mn.lastTimeSeen)); - } else if (strCommand == "activeseconds") { - obj.push_back(Pair(mn.addr.ToString().c_str(), (int64_t)(mn.lastTimeSeen - mn.now))); - } else if (strCommand == "rank") { - obj.push_back(Pair(mn.addr.ToString().c_str(), (int)(GetMasternodeRank(mn.vin, chainActive.Tip()->nHeight)))); - } - } - return obj; + if (params.size() == 2) return mnodeman.CountEnabled(); + return mnodeman.size(); } - if (strCommand == "count") return (int)vecMasternodes.size(); if (strCommand == "start") { @@ -455,9 +429,9 @@ Value masternode(const Array& params, bool fHelp) if (strCommand == "current") { - int winner = GetCurrentMasterNode(1); - if(winner >= 0) { - return vecMasternodes[winner].addr.ToString().c_str(); + CMasternode* winner = mnodeman.GetCurrentMasterNode(1); + if(winner) { + return winner->addr.ToString().c_str(); } return "unknown"; @@ -551,3 +525,85 @@ Value masternode(const Array& params, bool fHelp) return Value::null; } +Value masternodelist(const Array& params, bool fHelp) +{ + std::string strMode = "active"; + std::string strFilter = ""; + + if (params.size() >= 1) strMode = params[0].get_str(); + if (params.size() == 2) strFilter = params[1].get_str(); + + if (fHelp || + (strMode != "active" && strMode != "vin" && strMode != "pubkey" && strMode != "lastseen" + && strMode != "activeseconds" && strMode != "rank" && strMode != "protocol" && strMode != "full")) + { + throw runtime_error( + "masternodelist ( \"mode\" \"filter\" )\n" + "Get a list of masternodes in different modes\n" + "\nArguments:\n" + "1. \"mode\" (string, optional, defauls = active) The mode to run list in\n" + "2. \"filter\" (string, optional) Filter results, can be applied in few modes only\n" + "Available modes:\n" + " active - Print '1' if active and '0' otherwise (can be filtered, exact match)\n" + " activeseconds - Print number of seconds masternode recognized by the network as enabled\n" + " full - Print info in format 'active | protocol | pubkey | vin | lastseen | activeseconds' (can be filtered, partial match)\n" + " lastseen - Print timestamp of when a masternode was last seen on the network\n" + " protocol - Print protocol of a masternode (can be filtered, exact match)\n" + " pubkey - Print public key associated with a masternode (can be filtered, partial match)\n" + " rank - Print rank of a masternode based on current block\n" + " vin - Print vin associated with a masternode (can be filtered, partial match)\n" + ); + } + + Object obj; + std::vector vMasternodes = mnodeman.GetFullMasternodeVector(); + BOOST_FOREACH(CMasternode& mn, vMasternodes) { + + std::string strAddr = mn.addr.ToString().c_str(); + if(strMode == "active"){ + if(strFilter !="" && stoi(strFilter) != mn.IsEnabled()) continue; + obj.push_back(Pair(strAddr, (int)mn.IsEnabled())); + } else if (strMode == "vin") { + if(strFilter !="" && mn.vin.prevout.hash.ToString().find(strFilter) == string::npos) continue; + obj.push_back(Pair(strAddr, mn.vin.prevout.hash.ToString().c_str())); + } else if (strMode == "pubkey") { + CScript pubkey; + pubkey.SetDestination(mn.pubkey.GetID()); + CTxDestination address1; + ExtractDestination(pubkey, address1); + CBitcoinAddress address2(address1); + + if(strFilter !="" && address2.ToString().find(strFilter) == string::npos) continue; + obj.push_back(Pair(strAddr, address2.ToString().c_str())); + } else if (strMode == "protocol") { + if(strFilter !="" && stoi(strFilter) != mn.protocolVersion) continue; + obj.push_back(Pair(strAddr, (int64_t)mn.protocolVersion)); + } else if (strMode == "lastseen") { + obj.push_back(Pair(strAddr, (int64_t)mn.lastTimeSeen)); + } else if (strMode == "activeseconds") { + obj.push_back(Pair(strAddr, (int64_t)(mn.lastTimeSeen - mn.now))); + } else if (strMode == "rank") { + obj.push_back(Pair(strAddr, (int)(mnodeman.GetMasternodeRank(mn.vin, chainActive.Tip()->nHeight)))); + } else if (strMode == "full") { + CScript pubkey; + pubkey.SetDestination(mn.pubkey.GetID()); + CTxDestination address1; + ExtractDestination(pubkey, address1); + CBitcoinAddress address2(address1); + + std::ostringstream stringStream; + stringStream << (mn.IsEnabled() ? "1" : "0") << " | " << + mn.protocolVersion << " | " << + address2.ToString() << " | " << + mn.vin.prevout.hash.ToString() << " | " << + mn.lastTimeSeen << " | " << + (mn.lastTimeSeen - mn.now); + std::string output = stringStream.str(); + stringStream << " " << strAddr; + if(strFilter !="" && stringStream.str().find(strFilter) == string::npos) continue; + obj.push_back(Pair(strAddr, output)); + } + } + return obj; + +} diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index ba87446a023a7..afbdb69c51994 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -274,6 +274,7 @@ static const CRPCCommand vRPCCommands[] = { "spork", &spork, true, false, false }, { "masternode", &masternode, true, false, true }, + { "masternodelist", &masternodelist, true, false, false }, #ifdef ENABLE_WALLET /* Wallet */ { "addmultisigaddress", &addmultisigaddress, false, false, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 39a87e27acf26..9abe4df8713fd 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -193,6 +193,7 @@ extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHe extern json_spirit::Value darksend(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value spork(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value masternode(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value masternodelist(const json_spirit::Array& params, bool fHelp); #endif diff --git a/src/wallet.h b/src/wallet.h index 0719e18439b40..c8a79d4fd1af8 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -136,7 +136,6 @@ class CWallet : public CCryptoKeyStore, public CWalletInterface bool SelectCoinsDark(int64_t nValueMin, int64_t nValueMax, std::vector& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax) const; bool SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t nValueMax, std::vector& setCoinsRet, vector& vCoins, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax); bool SelectCoinsDarkDenominated(int64_t nTargetValue, std::vector& setCoinsRet, int64_t& nValueRet) const; - bool SelectCoinsMasternode(CTxIn& vin, int64_t& nValueRet, CScript& pubScript) const; bool HasCollateralInputs() const; bool IsCollateralAmount(int64_t nInputAmount) const; int CountInputsWithAmount(int64_t nInputAmount);