// Copyright (c) 2014-2017 The Dash Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef GOVERNANCE_H #define GOVERNANCE_H //#define ENABLE_DASH_DEBUG #include "cachemap.h" #include "cachemultimap.h" #include "chain.h" #include "governance-exceptions.h" #include "governance-object.h" #include "governance-vote.h" #include "net.h" #include "sync.h" #include "timedata.h" #include "util.h" class CGovernanceManager; class CGovernanceTriggerManager; class CGovernanceObject; class CGovernanceVote; extern std::map mapAskedForGovernanceObject; extern CGovernanceManager governance; typedef std::pair object_time_pair_t; static const int RATE_BUFFER_SIZE = 5; class CRateCheckBuffer { private: std::vector vecTimestamps; int nDataStart; int nDataEnd; bool fBufferEmpty; public: CRateCheckBuffer() : vecTimestamps(RATE_BUFFER_SIZE), nDataStart(0), nDataEnd(0), fBufferEmpty(true) {} void AddTimestamp(int64_t nTimestamp) { if((nDataEnd == nDataStart) && !fBufferEmpty) { // Buffer full, discard 1st element nDataStart = (nDataStart + 1) % RATE_BUFFER_SIZE; } vecTimestamps[nDataEnd] = nTimestamp; nDataEnd = (nDataEnd + 1) % RATE_BUFFER_SIZE; fBufferEmpty = false; } int64_t GetMinTimestamp() { int nIndex = nDataStart; int64_t nMin = numeric_limits::max(); if(fBufferEmpty) { return nMin; } do { if(vecTimestamps[nIndex] < nMin) { nMin = vecTimestamps[nIndex]; } nIndex = (nIndex + 1) % RATE_BUFFER_SIZE; } while(nIndex != nDataEnd); return nMin; } int64_t GetMaxTimestamp() { int nIndex = nDataStart; int64_t nMax = 0; if(fBufferEmpty) { return nMax; } do { if(vecTimestamps[nIndex] > nMax) { nMax = vecTimestamps[nIndex]; } nIndex = (nIndex + 1) % RATE_BUFFER_SIZE; } while(nIndex != nDataEnd); return nMax; } int GetCount() { int nCount = 0; if(fBufferEmpty) { return 0; } if(nDataEnd > nDataStart) { nCount = nDataEnd - nDataStart; } else { nCount = RATE_BUFFER_SIZE - nDataStart + nDataEnd; } return nCount; } double GetRate() { int nCount = GetCount(); if(nCount < 2) { return 0.0; } int64_t nMin = GetMinTimestamp(); int64_t nMax = GetMaxTimestamp(); if(nMin == nMax) { // multiple objects with the same timestamp => infinite rate return 1.0e10; } return double(nCount) / double(nMax - nMin); } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(vecTimestamps); READWRITE(nDataStart); READWRITE(nDataEnd); READWRITE(fBufferEmpty); } }; // // Governance Manager : Contains all proposals for the budget // class CGovernanceManager { friend class CGovernanceObject; public: // Types struct last_object_rec { last_object_rec(bool fStatusOKIn = true) : triggerBuffer(), watchdogBuffer(), fStatusOK(fStatusOKIn) {} ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(triggerBuffer); READWRITE(watchdogBuffer); READWRITE(fStatusOK); } CRateCheckBuffer triggerBuffer; CRateCheckBuffer watchdogBuffer; bool fStatusOK; }; typedef std::map object_m_t; typedef object_m_t::iterator object_m_it; typedef object_m_t::const_iterator object_m_cit; typedef CacheMap object_ref_cache_t; typedef std::map count_m_t; typedef count_m_t::iterator count_m_it; typedef count_m_t::const_iterator count_m_cit; typedef std::map vote_m_t; typedef vote_m_t::iterator vote_m_it; typedef vote_m_t::const_iterator vote_m_cit; typedef CacheMap vote_cache_t; typedef CacheMultiMap vote_mcache_t; typedef object_m_t::size_type size_type; typedef std::map txout_m_t; typedef txout_m_t::iterator txout_m_it; typedef txout_m_t::const_iterator txout_m_cit; typedef std::set hash_s_t; typedef hash_s_t::iterator hash_s_it; typedef hash_s_t::const_iterator hash_s_cit; typedef std::map object_time_m_t; typedef object_time_m_t::iterator object_time_m_it; typedef object_time_m_t::const_iterator object_time_m_cit; typedef std::map hash_time_m_t; typedef hash_time_m_t::iterator hash_time_m_it; typedef hash_time_m_t::const_iterator hash_time_m_cit; private: static const int MAX_CACHE_SIZE = 1000000; static const std::string SERIALIZATION_VERSION_STRING; // Keep track of current block index const CBlockIndex *pCurrentBlockIndex; int64_t nTimeLastDiff; int nCachedBlockHeight; // keep track of the scanning errors object_m_t mapObjects; count_m_t mapSeenGovernanceObjects; object_time_m_t mapMasternodeOrphanObjects; hash_time_m_t mapWatchdogObjects; object_ref_cache_t mapVoteToObject; vote_cache_t mapInvalidVotes; vote_mcache_t mapOrphanVotes; txout_m_t mapLastMasternodeObject; hash_s_t setRequestedObjects; hash_s_t setRequestedVotes; bool fRateChecksEnabled; public: // critical section to protect the inner data structures mutable CCriticalSection cs; CGovernanceManager(); virtual ~CGovernanceManager() {} int CountProposalInventoryItems() { // TODO What is this for ? return mapSeenGovernanceObjects.size(); //return mapSeenGovernanceObjects.size() + mapSeenVotes.size(); } /** * This is called by AlreadyHave in main.cpp as part of the inventory * retrieval process. Returns true if we want to retrieve the object, otherwise * false. (Note logic is inverted in AlreadyHave). */ bool ConfirmInventoryRequest(const CInv& inv); void Sync(CNode* node, uint256 nProp); void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); void NewBlock(); CGovernanceObject *FindGovernanceObject(const uint256& nHash); std::vector GetMatchingVotes(const uint256& nParentHash); std::vector GetCurrentVotes(const uint256& nParentHash, const CTxIn& mnCollateralOutpointFilter); std::vector GetAllNewerThan(int64_t nMoreThanTime); bool IsBudgetPaymentBlock(int nBlockHeight); bool AddGovernanceObject (CGovernanceObject& govobj); std::string GetRequiredPaymentsString(int nBlockHeight); void UpdateCachesAndClean(); void CheckAndRemove() {UpdateCachesAndClean();} void Clear() { LOCK(cs); LogPrint("gobject", "Governance object manager was cleared\n"); mapObjects.clear(); mapSeenGovernanceObjects.clear(); mapWatchdogObjects.clear(); mapVoteToObject.Clear(); mapInvalidVotes.Clear(); mapOrphanVotes.Clear(); mapLastMasternodeObject.clear(); } std::string ToString() const; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { LOCK(cs); std::string strVersion; if(ser_action.ForRead()) { READWRITE(strVersion); } else { strVersion = SERIALIZATION_VERSION_STRING; READWRITE(strVersion); } READWRITE(mapSeenGovernanceObjects); READWRITE(mapInvalidVotes); READWRITE(mapOrphanVotes); READWRITE(mapObjects); READWRITE(mapWatchdogObjects); READWRITE(mapLastMasternodeObject); if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { Clear(); return; } } void UpdatedBlockTip(const CBlockIndex *pindex); int64_t GetLastDiffTime() { return nTimeLastDiff; } void UpdateLastDiffTime(int64_t nTimeIn) { nTimeLastDiff = nTimeIn; } int GetCachedBlockHeight() { return nCachedBlockHeight; } // Accessors for thread-safe access to maps bool HaveObjectForHash(uint256 nHash); bool HaveVoteForHash(uint256 nHash); bool SerializeObjectForHash(uint256 nHash, CDataStream& ss); bool SerializeVoteForHash(uint256 nHash, CDataStream& ss); void AddSeenGovernanceObject(uint256 nHash, int status); void AddSeenVote(uint256 nHash, int status); bool MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateLast = false); bool MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateLast, bool fForce, bool& fRateCheckBypassed); bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception) { bool fOK = ProcessVote(NULL, vote, exception); if(fOK) { vote.Relay(); } return fOK; } void CheckMasternodeOrphanVotes(); void CheckMasternodeOrphanObjects(); bool AreRateChecksEnabled() const { LOCK(cs); return fRateChecksEnabled; } void InitOnLoad(); private: void RequestGovernanceObject(CNode* pfrom, const uint256& nHash); void AddInvalidVote(const CGovernanceVote& vote) { mapInvalidVotes.Insert(vote.GetHash(), vote); } void AddOrphanVote(const CGovernanceVote& vote) { mapOrphanVotes.Insert(vote.GetHash(), vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME)); } bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception); /// Called to indicate a requested object has been received bool AcceptObjectMessage(const uint256& nHash); /// Called to indicate a requested vote has been received bool AcceptVoteMessage(const uint256& nHash); static bool AcceptMessage(const uint256& nHash, hash_s_t& setHash); void CheckOrphanVotes(CGovernanceObject& govobj, CGovernanceException& exception); void RebuildIndexes(); /// Returns MN index, handling the case of index rebuilds int GetMasternodeIndex(const CTxIn& masternodeVin); void RebuildVoteMaps(); void AddCachedTriggers(); }; #endif