2016-04-09 21:57:53 +02:00
|
|
|
// Copyright (c) 2014-2016 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.
|
|
|
|
|
|
|
|
#include "core_io.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "init.h"
|
|
|
|
|
2016-04-10 08:31:32 +02:00
|
|
|
#include "flat-database.h"
|
2016-04-09 22:55:52 +02:00
|
|
|
#include "governance.h"
|
2016-11-24 19:12:05 +01:00
|
|
|
#include "governance-object.h"
|
2016-04-14 00:41:40 +02:00
|
|
|
#include "governance-vote.h"
|
2016-08-17 09:08:25 +02:00
|
|
|
#include "governance-classes.h"
|
2016-04-09 21:57:53 +02:00
|
|
|
#include "masternode.h"
|
2016-04-15 04:54:11 +02:00
|
|
|
#include "governance.h"
|
2016-04-09 21:57:53 +02:00
|
|
|
#include "darksend.h"
|
|
|
|
#include "masternodeman.h"
|
|
|
|
#include "masternode-sync.h"
|
2016-09-27 09:50:04 +02:00
|
|
|
#include "netfulfilledman.h"
|
2016-04-09 21:57:53 +02:00
|
|
|
#include "util.h"
|
|
|
|
#include "addrman.h"
|
|
|
|
#include <boost/lexical_cast.hpp>
|
2016-08-17 09:08:25 +02:00
|
|
|
#include <univalue.h>
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-04-10 16:46:19 +02:00
|
|
|
CGovernanceManager governance;
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-05-23 20:10:20 +02:00
|
|
|
std::map<uint256, int64_t> mapAskedForGovernanceObject;
|
2016-04-09 21:57:53 +02:00
|
|
|
|
|
|
|
int nSubmittedFinalBudget;
|
|
|
|
|
2016-11-29 01:01:03 +01:00
|
|
|
const std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-2";
|
2016-11-13 18:52:34 +01:00
|
|
|
|
2016-09-05 01:44:10 +02:00
|
|
|
CGovernanceManager::CGovernanceManager()
|
2016-11-13 18:52:34 +01:00
|
|
|
: pCurrentBlockIndex(NULL),
|
2016-09-05 01:44:10 +02:00
|
|
|
nTimeLastDiff(0),
|
|
|
|
nCachedBlockHeight(0),
|
|
|
|
mapObjects(),
|
|
|
|
mapSeenGovernanceObjects(),
|
2016-11-12 02:51:45 +01:00
|
|
|
mapMasternodeOrphanObjects(),
|
2016-11-25 15:08:48 +01:00
|
|
|
mapWatchdogObjects(),
|
2016-11-13 18:52:34 +01:00
|
|
|
mapVoteToObject(MAX_CACHE_SIZE),
|
|
|
|
mapInvalidVotes(MAX_CACHE_SIZE),
|
|
|
|
mapOrphanVotes(MAX_CACHE_SIZE),
|
2016-09-05 01:44:10 +02:00
|
|
|
mapLastMasternodeTrigger(),
|
2016-11-13 18:52:34 +01:00
|
|
|
setRequestedObjects(),
|
2016-11-18 15:17:22 +01:00
|
|
|
fRateChecksEnabled(true),
|
2016-09-05 01:44:10 +02:00
|
|
|
cs()
|
|
|
|
{}
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// Accessors for thread-safe access to maps
|
2016-09-12 09:40:00 +02:00
|
|
|
bool CGovernanceManager::HaveObjectForHash(uint256 nHash) {
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
|
|
|
return (mapObjects.count(nHash) == 1);
|
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
bool CGovernanceManager::SerializeObjectForHash(uint256 nHash, CDataStream& ss)
|
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
|
|
|
object_m_it it = mapObjects.find(nHash);
|
2016-09-12 09:40:00 +02:00
|
|
|
if (it == mapObjects.end()) {
|
2016-08-17 09:08:25 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ss << it->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
bool CGovernanceManager::HaveVoteForHash(uint256 nHash)
|
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
2016-11-13 18:52:34 +01:00
|
|
|
|
|
|
|
CGovernanceObject* pGovobj = NULL;
|
|
|
|
if(!mapVoteToObject.Get(nHash,pGovobj)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!pGovobj->GetVoteFile().HasVote(nHash)) {
|
2016-08-17 09:08:25 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
bool CGovernanceManager::SerializeVoteForHash(uint256 nHash, CDataStream& ss)
|
2016-08-17 09:08:25 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-11-13 18:52:34 +01:00
|
|
|
|
|
|
|
CGovernanceObject* pGovobj = NULL;
|
|
|
|
if(!mapVoteToObject.Get(nHash,pGovobj)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CGovernanceVote vote;
|
|
|
|
if(!pGovobj->GetVoteFile().GetVote(nHash, vote)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ss << vote;
|
|
|
|
return true;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
void CGovernanceManager::AddSeenGovernanceObject(uint256 nHash, int status)
|
2016-08-17 09:08:25 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-11-13 18:52:34 +01:00
|
|
|
mapSeenGovernanceObjects[nHash] = status;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
void CGovernanceManager::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
|
|
|
|
{
|
|
|
|
// lite mode is not supported
|
|
|
|
if(fLiteMode) return;
|
|
|
|
if(!masternodeSync.IsBlockchainSynced()) return;
|
|
|
|
|
2016-09-28 22:03:54 +02:00
|
|
|
if(pfrom->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) return;
|
2016-08-05 18:25:03 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// ANOTHER USER IS ASKING US TO HELP THEM SYNC GOVERNANCE OBJECT DATA
|
2016-06-08 08:57:16 +02:00
|
|
|
if (strCommand == NetMsgType::MNGOVERNANCESYNC)
|
|
|
|
{
|
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
// Ignore such requests until we are fully synced.
|
|
|
|
// We could start processing this after masternode list is synced
|
|
|
|
// but this is a heavy one so it's better to finish sync first.
|
2016-08-05 18:25:03 +02:00
|
|
|
if (!masternodeSync.IsSynced()) return;
|
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
uint256 nProp;
|
|
|
|
vRecv >> nProp;
|
|
|
|
|
2016-09-27 09:50:04 +02:00
|
|
|
if(nProp == uint256()) {
|
|
|
|
if(netfulfilledman.HasFulfilledRequest(pfrom->addr, NetMsgType::MNGOVERNANCESYNC)) {
|
|
|
|
// Asking for the whole list multiple times in a short period of time is no good
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "MNGOVERNANCESYNC -- peer already asked me for the list\n");
|
2016-09-27 09:50:04 +02:00
|
|
|
Misbehaving(pfrom->GetId(), 20);
|
|
|
|
return;
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
2016-09-27 09:50:04 +02:00
|
|
|
netfulfilledman.AddFulfilledRequest(pfrom->addr, NetMsgType::MNGOVERNANCESYNC);
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Sync(pfrom, nProp);
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "MNGOVERNANCESYNC -- syncing governance objects to our peer at %s\n", pfrom->addr.ToString());
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// A NEW GOVERNANCE OBJECT HAS ARRIVED
|
2016-06-08 08:57:16 +02:00
|
|
|
else if (strCommand == NetMsgType::MNGOVERNANCEOBJECT)
|
|
|
|
|
|
|
|
{
|
2016-06-10 07:16:32 +02:00
|
|
|
// MAKE SURE WE HAVE A VALID REFERENCE TO THE TIP BEFORE CONTINUING
|
|
|
|
|
2016-11-12 12:14:50 +01:00
|
|
|
if(!pCurrentBlockIndex) {
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrintf("MNGOVERNANCEOBJECT -- pCurrentBlockIndex is NULL\n");
|
2016-11-12 12:14:50 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-06-08 08:57:16 +02:00
|
|
|
|
|
|
|
CGovernanceObject govobj;
|
|
|
|
vRecv >> govobj;
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
uint256 nHash = govobj.GetHash();
|
|
|
|
std::string strHash = nHash.ToString();
|
|
|
|
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECT -- Received object: %s\n", strHash);
|
2016-11-13 18:52:34 +01:00
|
|
|
|
|
|
|
if(!AcceptObjectMessage(nHash)) {
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrintf("MNGOVERNANCEOBJECT -- Received unrequested object: %s\n", strHash);
|
2016-11-13 18:52:34 +01:00
|
|
|
Misbehaving(pfrom->GetId(), 20);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-28 15:21:50 +01:00
|
|
|
LOCK(cs);
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
if(mapSeenGovernanceObjects.count(nHash)) {
|
2016-06-08 08:57:16 +02:00
|
|
|
// TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE?
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECT -- Received already seen object: %s\n", strHash);
|
2016-06-08 08:57:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string strError = "";
|
2016-09-12 09:40:00 +02:00
|
|
|
// CHECK OBJECT AGAINST LOCAL BLOCKCHAIN
|
2016-08-17 09:08:25 +02:00
|
|
|
|
2016-11-12 02:51:45 +01:00
|
|
|
bool fMasternodeMissing = false;
|
|
|
|
bool fIsValid = govobj.IsValidLocally(pCurrentBlockIndex, strError, fMasternodeMissing, true);
|
|
|
|
|
|
|
|
if(fMasternodeMissing) {
|
2016-12-06 17:40:37 +01:00
|
|
|
mapMasternodeOrphanObjects.insert(std::make_pair(nHash, object_time_pair_t(govobj, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME)));
|
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECT -- Missing masternode for: %s\n", strHash);
|
2016-11-12 02:51:45 +01:00
|
|
|
// fIsValid must also be false here so we will return early in the next if block
|
|
|
|
}
|
|
|
|
if(!fIsValid) {
|
2016-11-13 18:52:34 +01:00
|
|
|
mapSeenGovernanceObjects.insert(std::make_pair(nHash, SEEN_OBJECT_ERROR_INVALID));
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("MNGOVERNANCEOBJECT -- Governance object is invalid - %s\n", strError);
|
2016-06-08 08:57:16 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-06-10 07:16:32 +02:00
|
|
|
// UPDATE CACHED VARIABLES FOR THIS OBJECT AND ADD IT TO OUR MANANGED DATA
|
2016-06-08 08:57:16 +02:00
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
govobj.UpdateSentinelVariables(pCurrentBlockIndex); //this sets local vars in object
|
2016-08-17 09:08:25 +02:00
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
if(AddGovernanceObject(govobj))
|
|
|
|
{
|
2016-11-13 18:52:34 +01:00
|
|
|
LogPrintf("MNGOVERNANCEOBJECT -- %s new\n", strHash);
|
2016-10-22 18:52:14 +02:00
|
|
|
govobj.Relay();
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
2016-06-08 08:57:16 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// UPDATE THAT WE'VE SEEN THIS OBJECT
|
2016-12-06 17:40:37 +01:00
|
|
|
mapSeenGovernanceObjects.insert(std::make_pair(nHash, SEEN_OBJECT_IS_VALID));
|
|
|
|
masternodeSync.AddedGovernanceItem();
|
2016-06-08 08:57:16 +02:00
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// WE MIGHT HAVE PENDING/ORPHAN VOTES FOR THIS OBJECT
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
CGovernanceException exception;
|
2016-12-02 13:53:18 +01:00
|
|
|
CheckOrphanVotes(govobj, exception);
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// A NEW GOVERNANCE OBJECT VOTE HAS ARRIVED
|
2016-08-05 18:25:03 +02:00
|
|
|
else if (strCommand == NetMsgType::MNGOVERNANCEOBJECTVOTE)
|
2016-06-08 08:57:16 +02:00
|
|
|
{
|
2016-08-29 21:11:34 +02:00
|
|
|
// Ignore such messages until masternode list is synced
|
2016-11-12 12:14:50 +01:00
|
|
|
if(!masternodeSync.IsMasternodeListSynced()) {
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n");
|
2016-11-12 12:14:50 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-08-29 21:11:34 +02:00
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
CGovernanceVote vote;
|
|
|
|
vRecv >> vote;
|
|
|
|
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- Received vote: %s\n", vote.ToString());
|
2016-08-17 09:08:25 +02:00
|
|
|
|
2016-12-06 17:40:37 +01:00
|
|
|
uint256 nHash = vote.GetHash();
|
|
|
|
std::string strHash = nHash.ToString();
|
|
|
|
|
|
|
|
if(!AcceptVoteMessage(nHash)) {
|
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n",
|
|
|
|
vote.ToString(), strHash, pfrom->GetId());
|
2016-11-13 18:52:34 +01:00
|
|
|
//Misbehaving(pfrom->GetId(), 20);
|
2016-06-08 08:57:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
CGovernanceException exception;
|
|
|
|
if(ProcessVote(pfrom, vote, exception)) {
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- %s new\n", strHash);
|
|
|
|
masternodeSync.AddedGovernanceItem();
|
2016-12-07 05:16:34 +01:00
|
|
|
vote.Relay();
|
2016-11-13 18:52:34 +01:00
|
|
|
}
|
|
|
|
else {
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- Rejected vote, error = %s\n", exception.what());
|
2016-11-13 18:52:34 +01:00
|
|
|
if((exception.GetNodePenalty() != 0) && masternodeSync.IsSynced()) {
|
|
|
|
Misbehaving(pfrom->GetId(), exception.GetNodePenalty());
|
|
|
|
}
|
|
|
|
return;
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-02 13:53:18 +01:00
|
|
|
void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj, CGovernanceException& exception)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-11-13 18:52:34 +01:00
|
|
|
uint256 nHash = govobj.GetHash();
|
2016-11-24 19:12:05 +01:00
|
|
|
std::vector<vote_time_pair_t> vecVotePairs;
|
|
|
|
mapOrphanVotes.GetAll(nHash, vecVotePairs);
|
|
|
|
|
|
|
|
int64_t nNow = GetAdjustedTime();
|
|
|
|
for(size_t i = 0; i < vecVotePairs.size(); ++i) {
|
|
|
|
bool fRemove = false;
|
|
|
|
vote_time_pair_t& pairVote = vecVotePairs[i];
|
|
|
|
CGovernanceVote& vote = pairVote.first;
|
2016-11-13 18:52:34 +01:00
|
|
|
CGovernanceException exception;
|
2016-11-24 19:12:05 +01:00
|
|
|
if(pairVote.second < nNow) {
|
|
|
|
fRemove = true;
|
|
|
|
}
|
2016-12-02 13:53:18 +01:00
|
|
|
else if(govobj.ProcessVote(NULL, vote, exception)) {
|
2016-11-24 19:12:05 +01:00
|
|
|
fRemove = true;
|
2016-11-13 18:52:34 +01:00
|
|
|
}
|
2016-11-24 19:12:05 +01:00
|
|
|
if(fRemove) {
|
|
|
|
mapOrphanVotes.Erase(nHash, pairVote);
|
|
|
|
}
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-28 12:31:44 +02:00
|
|
|
bool CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
std::string strError = "";
|
2016-08-17 09:08:25 +02:00
|
|
|
|
|
|
|
DBG( cout << "CGovernanceManager::AddGovernanceObject START" << endl; );
|
|
|
|
|
2016-11-25 15:08:48 +01:00
|
|
|
uint256 nHash = govobj.GetHash();
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// MAKE SURE THIS OBJECT IS OK
|
|
|
|
|
|
|
|
if(!govobj.IsValidLocally(pCurrentBlockIndex, strError, true)) {
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceManager::AddGovernanceObject -- invalid governance object - %s - (pCurrentBlockIndex nHeight %d) \n", strError, pCurrentBlockIndex->nHeight);
|
2016-04-09 21:57:53 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF WE HAVE THIS OBJECT ALREADY, WE DON'T WANT ANOTHER COPY
|
|
|
|
|
2016-11-25 15:08:48 +01:00
|
|
|
if(mapObjects.count(nHash)) {
|
2016-11-30 02:33:47 +01:00
|
|
|
LogPrintf("CGovernanceManager::AddGovernanceObject -- already have governance object %s\n", nHash.ToString());
|
2016-04-09 21:57:53 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// INSERT INTO OUR GOVERNANCE OBJECT MEMORY
|
2016-11-25 15:08:48 +01:00
|
|
|
mapObjects.insert(std::make_pair(nHash, govobj));
|
2016-08-17 09:08:25 +02:00
|
|
|
|
|
|
|
// SHOULD WE ADD THIS OBJECT TO ANY OTHER MANANGERS?
|
|
|
|
|
|
|
|
DBG( cout << "CGovernanceManager::AddGovernanceObject Before trigger block, strData = "
|
|
|
|
<< govobj.GetDataAsString()
|
|
|
|
<< ", nObjectType = " << govobj.nObjectType
|
|
|
|
<< endl; );
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
if(govobj.GetObjectType() == GOVERNANCE_OBJECT_TRIGGER) {
|
|
|
|
mapLastMasternodeTrigger[govobj.GetMasternodeVin().prevout] = nCachedBlockHeight;
|
|
|
|
}
|
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
switch(govobj.nObjectType) {
|
|
|
|
case GOVERNANCE_OBJECT_TRIGGER:
|
2016-09-15 08:49:24 +02:00
|
|
|
mapLastMasternodeTrigger[govobj.vinMasternode.prevout] = nCachedBlockHeight;
|
2016-08-17 09:08:25 +02:00
|
|
|
DBG( cout << "CGovernanceManager::AddGovernanceObject Before AddNewTrigger" << endl; );
|
2016-11-25 15:08:48 +01:00
|
|
|
triggerman.AddNewTrigger(nHash);
|
2016-08-17 09:08:25 +02:00
|
|
|
DBG( cout << "CGovernanceManager::AddGovernanceObject After AddNewTrigger" << endl; );
|
2016-10-17 20:54:28 +02:00
|
|
|
break;
|
2016-11-25 15:08:48 +01:00
|
|
|
case GOVERNANCE_OBJECT_WATCHDOG:
|
|
|
|
mapWatchdogObjects[nHash] = GetAdjustedTime() + GOVERNANCE_WATCHDOG_EXPIRATION_TIME;
|
|
|
|
break;
|
2016-10-17 20:54:28 +02:00
|
|
|
default:
|
|
|
|
break;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DBG( cout << "CGovernanceManager::AddGovernanceObject END" << endl; );
|
|
|
|
|
2016-04-09 21:57:53 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
void CGovernanceManager::UpdateCachesAndClean()
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "CGovernanceManager::UpdateCachesAndClean\n");
|
2016-08-17 09:08:25 +02:00
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
std::vector<uint256> vecDirtyHashes = mnodeman.GetAndClearDirtyGovernanceObjectHashes();
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-11-25 15:08:48 +01:00
|
|
|
// Flag expired watchdogs for removal
|
|
|
|
int64_t nNow = GetAdjustedTime();
|
|
|
|
if(mapWatchdogObjects.size() > 1) {
|
|
|
|
hash_time_m_it it = mapWatchdogObjects.begin();
|
|
|
|
while(it != mapWatchdogObjects.end()) {
|
|
|
|
if(it->second < nNow) {
|
|
|
|
object_m_it it2 = mapObjects.find(it->first);
|
|
|
|
if(it2 != mapObjects.end()) {
|
|
|
|
it2->second.fExpired = true;
|
|
|
|
it2->second.nDeletionTime = nNow;
|
|
|
|
}
|
|
|
|
mapWatchdogObjects.erase(it++);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
for(size_t i = 0; i < vecDirtyHashes.size(); ++i) {
|
|
|
|
object_m_it it = mapObjects.find(vecDirtyHashes[i]);
|
|
|
|
if(it == mapObjects.end()) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-11-13 18:52:34 +01:00
|
|
|
it->second.ClearMasternodeVotes();
|
2016-10-17 20:54:28 +02:00
|
|
|
it->second.fDirtyCache = true;
|
|
|
|
}
|
|
|
|
|
2016-06-10 07:16:32 +02:00
|
|
|
// DOUBLE CHECK THAT WE HAVE A VALID POINTER TO TIP
|
|
|
|
|
|
|
|
if(!pCurrentBlockIndex) return;
|
|
|
|
|
2016-12-04 21:33:39 +01:00
|
|
|
fRateChecksEnabled = false;
|
|
|
|
|
2016-11-05 17:13:30 +01:00
|
|
|
LogPrint("gobject", "CGovernanceManager::UpdateCachesAndClean -- After pCurrentBlockIndex (not NULL)\n");
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// UPDATE CACHE FOR EACH OBJECT THAT IS FLAGGED DIRTYCACHE=TRUE
|
2016-05-24 20:11:59 +02:00
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
object_m_it it = mapObjects.begin();
|
2016-08-17 09:08:25 +02:00
|
|
|
|
|
|
|
// Clean up any expired or invalid triggers
|
|
|
|
triggerman.CleanAndRemove();
|
|
|
|
|
2016-05-24 20:11:59 +02:00
|
|
|
while(it != mapObjects.end())
|
2016-09-12 09:40:00 +02:00
|
|
|
{
|
2016-05-24 20:11:59 +02:00
|
|
|
CGovernanceObject* pObj = &((*it).second);
|
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
if(!pObj) {
|
2016-08-17 09:08:25 +02:00
|
|
|
++it;
|
|
|
|
continue;
|
|
|
|
}
|
2016-05-24 20:11:59 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF CACHE IS NOT DIRTY, WHY DO THIS?
|
2016-11-13 18:52:34 +01:00
|
|
|
if(pObj->IsSetDirtyCache()) {
|
2016-08-17 09:08:25 +02:00
|
|
|
// UPDATE LOCAL VALIDITY AGAINST CRYPTO DATA
|
|
|
|
pObj->UpdateLocalValidity(pCurrentBlockIndex);
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// UPDATE SENTINEL SIGNALING VARIABLES
|
|
|
|
pObj->UpdateSentinelVariables(pCurrentBlockIndex);
|
|
|
|
}
|
2016-04-09 22:55:52 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF DELETE=TRUE, THEN CLEAN THE MESS UP!
|
2016-05-24 20:11:59 +02:00
|
|
|
|
2016-11-18 15:17:00 +01:00
|
|
|
int64_t nTimeSinceDeletion = GetAdjustedTime() - pObj->GetDeletionTime();
|
|
|
|
|
|
|
|
if((pObj->IsSetCachedDelete() || pObj->IsSetExpired()) &&
|
|
|
|
(nTimeSinceDeletion >= GOVERNANCE_DELETION_DELAY)) {
|
2016-11-05 17:13:30 +01:00
|
|
|
LogPrintf("CGovernanceManager::UpdateCachesAndClean -- erase obj %s\n", (*it).first.ToString());
|
2016-10-17 20:54:28 +02:00
|
|
|
mnodeman.RemoveGovernanceObject(pObj->GetHash());
|
2016-11-13 18:52:34 +01:00
|
|
|
|
|
|
|
// Remove vote references
|
|
|
|
const object_ref_cache_t::list_t& listItems = mapVoteToObject.GetItemList();
|
|
|
|
object_ref_cache_t::list_cit lit = listItems.begin();
|
|
|
|
while(lit != listItems.end()) {
|
|
|
|
if(lit->value == pObj) {
|
|
|
|
uint256 nKey = lit->key;
|
|
|
|
++lit;
|
|
|
|
mapVoteToObject.Erase(nKey);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++lit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
mapObjects.erase(it++);
|
|
|
|
} else {
|
|
|
|
++it;
|
|
|
|
}
|
2016-05-23 23:39:10 +02:00
|
|
|
}
|
2016-12-04 21:33:39 +01:00
|
|
|
|
|
|
|
fRateChecksEnabled = true;
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
CGovernanceObject *CGovernanceManager::FindGovernanceObject(const uint256& nHash)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
|
2016-05-13 18:03:01 +02:00
|
|
|
if(mapObjects.count(nHash))
|
|
|
|
return &mapObjects[nHash];
|
2016-04-09 21:57:53 +02:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
std::vector<CGovernanceVote> CGovernanceManager::GetMatchingVotes(const uint256& nParentHash)
|
2016-08-17 09:08:25 +02:00
|
|
|
{
|
2016-11-13 18:52:34 +01:00
|
|
|
LOCK(cs);
|
|
|
|
std::vector<CGovernanceVote> vecResult;
|
2016-08-17 09:08:25 +02:00
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
object_m_it it = mapObjects.find(nParentHash);
|
|
|
|
if(it == mapObjects.end()) {
|
|
|
|
return vecResult;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
2016-11-13 18:52:34 +01:00
|
|
|
CGovernanceObject& govobj = it->second;
|
2016-08-17 09:08:25 +02:00
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
return govobj.GetVoteFile().GetVotes();
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
2016-11-22 20:26:36 +01:00
|
|
|
std::vector<CGovernanceVote> CGovernanceManager::GetCurrentVotes(const uint256& nParentHash, const CTxIn& mnCollateralOutpointFilter)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
std::vector<CGovernanceVote> vecResult;
|
|
|
|
|
|
|
|
// Find the governance object or short-circuit.
|
|
|
|
object_m_it it = mapObjects.find(nParentHash);
|
|
|
|
if(it == mapObjects.end()) return vecResult;
|
|
|
|
CGovernanceObject& govobj = it->second;
|
|
|
|
|
|
|
|
// Compile a list of Masternode collateral outpoints for which to get votes
|
|
|
|
std::vector<CTxIn> vecMNTxIn;
|
|
|
|
if (mnCollateralOutpointFilter == CTxIn()) {
|
|
|
|
std::vector<CMasternode> mnlist = mnodeman.GetFullMasternodeVector();
|
|
|
|
for (std::vector<CMasternode>::iterator it = mnlist.begin(); it != mnlist.end(); ++it)
|
|
|
|
{
|
|
|
|
vecMNTxIn.push_back(it->vin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
vecMNTxIn.push_back(mnCollateralOutpointFilter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop thru each MN collateral outpoint and get the votes for the `nParentHash` governance object
|
|
|
|
for (std::vector<CTxIn>::iterator it = vecMNTxIn.begin(); it != vecMNTxIn.end(); ++it)
|
|
|
|
{
|
|
|
|
CTxIn &mnCollateralOutpoint = *it;
|
|
|
|
|
|
|
|
// get a vote_rec_t from the govobj
|
|
|
|
vote_rec_t voteRecord;
|
|
|
|
if (!govobj.GetCurrentMNVotes(mnCollateralOutpoint, voteRecord)) continue;
|
|
|
|
|
|
|
|
for (vote_instance_m_it it3 = voteRecord.mapInstances.begin(); it3 != voteRecord.mapInstances.end(); ++it3) {
|
|
|
|
int signal = (it3->first);
|
|
|
|
int outcome = ((it3->second).eOutcome);
|
|
|
|
int64_t nTime = ((it3->second).nTime);
|
|
|
|
|
|
|
|
CGovernanceVote vote = CGovernanceVote(mnCollateralOutpoint, nParentHash, (vote_signal_enum_t)signal, (vote_outcome_enum_t)outcome);
|
|
|
|
vote.SetTime(nTime);
|
|
|
|
|
|
|
|
vecResult.push_back(vote);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return vecResult;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
std::vector<CGovernanceObject*> CGovernanceManager::GetAllNewerThan(int64_t nMoreThanTime)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
|
2016-05-28 12:31:44 +02:00
|
|
|
std::vector<CGovernanceObject*> vGovObjs;
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
object_m_it it = mapObjects.begin();
|
2016-05-13 18:03:01 +02:00
|
|
|
while(it != mapObjects.end())
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF THIS OBJECT IS OLDER THAN TIME, CONTINUE
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
if((*it).second.GetCreationTime() < nMoreThanTime) {
|
2016-08-17 09:08:25 +02:00
|
|
|
++it;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ADD GOVERNANCE OBJECT TO LIST
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-05-28 12:31:44 +02:00
|
|
|
CGovernanceObject* pGovObj = &((*it).second);
|
|
|
|
vGovObjs.push_back(pGovObj);
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// NEXT
|
|
|
|
|
2016-04-09 21:57:53 +02:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
2016-05-28 12:31:44 +02:00
|
|
|
return vGovObjs;
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Sort by votes, if there's a tie sort by their feeHash TX
|
|
|
|
//
|
|
|
|
struct sortProposalsByVotes {
|
2016-04-16 19:19:17 +02:00
|
|
|
bool operator()(const std::pair<CGovernanceObject*, int> &left, const std::pair<CGovernanceObject*, int> &right) {
|
2016-09-12 09:40:00 +02:00
|
|
|
if (left.second != right.second)
|
|
|
|
return (left.second > right.second);
|
2016-11-13 18:52:34 +01:00
|
|
|
return (UintToArith256(left.first->GetCollateralHash()) > UintToArith256(right.first->GetCollateralHash()));
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-04-09 22:55:52 +02:00
|
|
|
void CGovernanceManager::NewBlock()
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF WE'RE NOT SYNCED, EXIT
|
|
|
|
if(!masternodeSync.IsSynced()) return;
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-29 21:11:34 +02:00
|
|
|
TRY_LOCK(cs, fBudgetNewBlock);
|
|
|
|
if(!fBudgetNewBlock || !pCurrentBlockIndex) return;
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-05-23 20:10:20 +02:00
|
|
|
std::map<uint256, int64_t>::iterator it = mapAskedForGovernanceObject.begin();
|
2016-11-13 18:52:34 +01:00
|
|
|
while(it != mapAskedForGovernanceObject.end()) {
|
|
|
|
if((*it).second > GetTime() - (60*60*24)) {
|
2016-04-09 21:57:53 +02:00
|
|
|
++it;
|
|
|
|
} else {
|
2016-05-23 20:10:20 +02:00
|
|
|
mapAskedForGovernanceObject.erase(it++);
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// CHECK AND REMOVE - REPROCESS GOVERNANCE OBJECTS
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
UpdateCachesAndClean();
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
|
|
|
|
LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest inv = %s\n", inv.ToString());
|
|
|
|
|
|
|
|
// First check if we've already recorded this object
|
|
|
|
switch(inv.type) {
|
|
|
|
case MSG_GOVERNANCE_OBJECT:
|
|
|
|
{
|
|
|
|
object_m_it it = mapObjects.find(inv.hash);
|
|
|
|
if(it != mapObjects.end()) {
|
|
|
|
LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest already have governance object, returning false\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MSG_GOVERNANCE_OBJECT_VOTE:
|
|
|
|
{
|
|
|
|
if(mapVoteToObject.HasKey(inv.hash)) {
|
|
|
|
LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest already have governance vote, returning false\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest unknown type, returning false\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
hash_s_t* setHash = NULL;
|
|
|
|
switch(inv.type) {
|
|
|
|
case MSG_GOVERNANCE_OBJECT:
|
|
|
|
setHash = &setRequestedObjects;
|
|
|
|
break;
|
|
|
|
case MSG_GOVERNANCE_OBJECT_VOTE:
|
|
|
|
setHash = &setRequestedVotes;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hash_s_cit it = setHash->find(inv.hash);
|
|
|
|
if(it == setHash->end()) {
|
|
|
|
setHash->insert(inv.hash);
|
|
|
|
LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest added inv to requested set\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest reached end, returning true\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-14 00:41:40 +02:00
|
|
|
void CGovernanceManager::Sync(CNode* pfrom, uint256 nProp)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
This code checks each of the hash maps for all known budget proposals and finalized budget proposals, then checks them against the
|
|
|
|
budget object to see if they're OK. If all checks pass, we'll send it to the peer.
|
|
|
|
*/
|
|
|
|
|
2016-12-06 17:40:37 +01:00
|
|
|
int nObjCount = 0;
|
|
|
|
int nVoteCount = 0;
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-05-24 20:11:59 +02:00
|
|
|
// SYNC GOVERNANCE OBJECTS WITH OTHER CLIENT
|
|
|
|
|
2016-12-04 21:33:39 +01:00
|
|
|
LogPrint("gobject", "CGovernanceManager::Sync -- syncing to peer=%d, nProp = %s\n", pfrom->id, nProp.ToString());
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
{
|
2016-11-13 18:52:34 +01:00
|
|
|
LOCK(cs);
|
2016-12-04 21:33:39 +01:00
|
|
|
fRateChecksEnabled = false;
|
2016-11-13 18:52:34 +01:00
|
|
|
for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) {
|
|
|
|
uint256 h = it->first;
|
|
|
|
|
|
|
|
CGovernanceObject& govobj = it->second;
|
|
|
|
|
2016-12-04 21:33:39 +01:00
|
|
|
if((nProp != uint256()) && (h != nProp)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-12-06 17:40:37 +01:00
|
|
|
std::string strHash = h.ToString();
|
|
|
|
|
|
|
|
LogPrint("gobject", "CGovernanceManager::Sync -- attempting to sync govobj: %s, peer=%d\n", strHash, pfrom->id);
|
2016-12-04 21:33:39 +01:00
|
|
|
|
2016-11-15 03:27:05 +01:00
|
|
|
std::string strError;
|
2016-12-04 21:33:39 +01:00
|
|
|
bool fIsValid = govobj.IsValidLocally(pCurrentBlockIndex, strError, true);
|
|
|
|
if(!fIsValid) {
|
|
|
|
LogPrintf("CGovernanceManager::Sync -- not syncing invalid govobj: %s, strError = %s, fCachedValid = %d, peer=%d\n",
|
2016-12-06 17:40:37 +01:00
|
|
|
strHash, strError, govobj.IsSetCachedValid(), pfrom->id);
|
2016-12-04 21:33:39 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!govobj.IsSetCachedValid()) {
|
|
|
|
LogPrintf("CGovernanceManager::Sync -- invalid flag cached, not syncing govobj: %s, fCachedValid = %d, peer=%d\n",
|
2016-12-06 17:40:37 +01:00
|
|
|
strHash, govobj.IsSetCachedValid(), pfrom->id);
|
2016-12-04 21:33:39 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push the inventory budget proposal message over to the other client
|
2016-12-06 17:40:37 +01:00
|
|
|
LogPrint("gobject", "CGovernanceManager::Sync -- syncing govobj: %s, peer=%d\n", strHash, pfrom->id);
|
2016-12-04 21:33:39 +01:00
|
|
|
pfrom->PushInventory(CInv(MSG_GOVERNANCE_OBJECT, h));
|
2016-12-06 17:40:37 +01:00
|
|
|
++nObjCount;
|
2016-11-13 18:52:34 +01:00
|
|
|
|
2016-12-04 21:33:39 +01:00
|
|
|
std::vector<CGovernanceVote> vecVotes = govobj.GetVoteFile().GetVotes();
|
|
|
|
for(size_t i = 0; i < vecVotes.size(); ++i) {
|
|
|
|
if(!vecVotes[i].IsValid(true)) {
|
|
|
|
continue;
|
2016-11-13 18:52:34 +01:00
|
|
|
}
|
2016-12-04 21:33:39 +01:00
|
|
|
pfrom->PushInventory(CInv(MSG_GOVERNANCE_OBJECT_VOTE, vecVotes[i].GetHash()));
|
2016-12-06 17:40:37 +01:00
|
|
|
++nVoteCount;
|
2016-11-13 18:52:34 +01:00
|
|
|
}
|
|
|
|
}
|
2016-12-04 21:33:39 +01:00
|
|
|
fRateChecksEnabled = true;
|
2016-04-14 00:41:40 +02:00
|
|
|
}
|
|
|
|
|
2016-12-06 17:40:37 +01:00
|
|
|
pfrom->PushMessage(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ, nObjCount);
|
|
|
|
pfrom->PushMessage(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ_VOTE, nVoteCount);
|
|
|
|
LogPrintf("CGovernanceManager::Sync -- sent %d objects and %d votes to peer=%d\n", nObjCount, nVoteCount, pfrom->id);
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
bool CGovernanceManager::MasternodeRateCheck(const CTxIn& vin, int nObjectType)
|
2016-09-05 01:44:10 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-10-17 20:54:28 +02:00
|
|
|
|
2016-11-18 15:17:22 +01:00
|
|
|
if(!fRateChecksEnabled) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
int mindiff = 0;
|
|
|
|
switch(nObjectType) {
|
|
|
|
case GOVERNANCE_OBJECT_TRIGGER:
|
|
|
|
mindiff = Params().GetConsensus().nSuperblockCycle - Params().GetConsensus().nSuperblockCycle / 10;
|
|
|
|
break;
|
|
|
|
case GOVERNANCE_OBJECT_WATCHDOG:
|
|
|
|
mindiff = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-09-15 08:49:24 +02:00
|
|
|
txout_m_it it = mapLastMasternodeTrigger.find(vin.prevout);
|
2016-09-12 09:40:00 +02:00
|
|
|
if(it == mapLastMasternodeTrigger.end()) {
|
2016-09-05 01:44:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Allow 1 trigger per mn per cycle, with a small fudge factor
|
2016-09-12 09:40:00 +02:00
|
|
|
if((nCachedBlockHeight - it->second) > mindiff) {
|
2016-09-05 01:44:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
2016-09-15 08:49:24 +02:00
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceManager::MasternodeRateCheck -- Rate too high: vin = %s, current height = %d, last MN height = %d, minimum difference = %d\n",
|
2016-09-15 08:49:24 +02:00
|
|
|
vin.prevout.ToStringShort(), nCachedBlockHeight, it->second, mindiff);
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
bool CGovernanceManager::ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
uint256 nHashVote = vote.GetHash();
|
|
|
|
if(mapInvalidVotes.HasKey(nHashVote)) {
|
|
|
|
std::ostringstream ostr;
|
|
|
|
ostr << "CGovernanceManager::ProcessVote -- Old invalid vote "
|
|
|
|
<< ", MN outpoint = " << vote.GetVinMasternode().prevout.ToStringShort()
|
|
|
|
<< ", governance object hash = " << vote.GetParentHash().ToString() << "\n";
|
|
|
|
LogPrintf(ostr.str().c_str());
|
|
|
|
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint256 nHashGovobj = vote.GetParentHash();
|
|
|
|
object_m_it it = mapObjects.find(nHashGovobj);
|
|
|
|
if(it == mapObjects.end()) {
|
2016-11-14 21:26:53 +01:00
|
|
|
std::ostringstream ostr;
|
|
|
|
ostr << "CGovernanceManager::ProcessVote -- Unknown parent object "
|
|
|
|
<< ", MN outpoint = " << vote.GetVinMasternode().prevout.ToStringShort()
|
|
|
|
<< ", governance object hash = " << vote.GetParentHash().ToString() << "\n";
|
|
|
|
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING);
|
2016-11-24 19:12:05 +01:00
|
|
|
if(mapOrphanVotes.Insert(nHashGovobj, vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) {
|
2016-11-14 19:22:50 +01:00
|
|
|
RequestGovernanceObject(pfrom, nHashGovobj);
|
|
|
|
LogPrintf(ostr.str().c_str());
|
2016-11-14 21:26:53 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
LogPrint("gobject", ostr.str().c_str());
|
2016-11-14 19:22:50 +01:00
|
|
|
}
|
2016-11-13 18:52:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CGovernanceObject& govobj = it->second;
|
|
|
|
bool fOk = govobj.ProcessVote(pfrom, vote, exception);
|
|
|
|
if(fOk) {
|
2016-12-06 17:40:37 +01:00
|
|
|
mapVoteToObject.Insert(nHashVote, &govobj);
|
2016-11-13 18:52:34 +01:00
|
|
|
|
|
|
|
if(govobj.GetObjectType() == GOVERNANCE_OBJECT_WATCHDOG) {
|
|
|
|
mnodeman.UpdateWatchdogVoteTime(vote.GetVinMasternode());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fOk;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceManager::CheckMasternodeOrphanVotes()
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-11-18 15:17:22 +01:00
|
|
|
fRateChecksEnabled = false;
|
2016-11-13 18:52:34 +01:00
|
|
|
for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) {
|
|
|
|
it->second.CheckOrphanVotes();
|
|
|
|
}
|
2016-11-18 15:17:22 +01:00
|
|
|
fRateChecksEnabled = true;
|
2016-11-13 18:52:34 +01:00
|
|
|
}
|
|
|
|
|
2016-11-12 02:51:45 +01:00
|
|
|
void CGovernanceManager::CheckMasternodeOrphanObjects()
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-11-24 19:12:05 +01:00
|
|
|
int64_t nNow = GetAdjustedTime();
|
2016-11-18 15:17:22 +01:00
|
|
|
fRateChecksEnabled = false;
|
2016-11-24 19:12:05 +01:00
|
|
|
object_time_m_it it = mapMasternodeOrphanObjects.begin();
|
2016-11-12 02:51:45 +01:00
|
|
|
while(it != mapMasternodeOrphanObjects.end()) {
|
2016-11-24 19:12:05 +01:00
|
|
|
object_time_pair_t& pair = it->second;
|
|
|
|
CGovernanceObject& govobj = pair.first;
|
|
|
|
|
|
|
|
if(pair.second < nNow) {
|
|
|
|
mapMasternodeOrphanObjects.erase(it++);
|
|
|
|
continue;
|
|
|
|
}
|
2016-11-12 02:51:45 +01:00
|
|
|
|
|
|
|
string strError;
|
|
|
|
bool fMasternodeMissing = false;
|
|
|
|
bool fIsValid = govobj.IsValidLocally(pCurrentBlockIndex, strError, fMasternodeMissing, true);
|
|
|
|
if(!fIsValid) {
|
|
|
|
if(!fMasternodeMissing) {
|
|
|
|
mapMasternodeOrphanObjects.erase(it++);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++it;
|
|
|
|
}
|
2016-11-16 23:28:10 +01:00
|
|
|
continue;
|
2016-11-12 02:51:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(AddGovernanceObject(govobj)) {
|
|
|
|
LogPrintf("CGovernanceManager::CheckMasternodeOrphanObjects -- %s new\n", govobj.GetHash().ToString());
|
|
|
|
mapMasternodeOrphanObjects.erase(it++);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
2016-11-18 15:17:22 +01:00
|
|
|
fRateChecksEnabled = true;
|
2016-11-12 02:51:45 +01:00
|
|
|
}
|
|
|
|
|
2016-11-13 18:52:34 +01:00
|
|
|
void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nHash)
|
|
|
|
{
|
|
|
|
if(!pfrom) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pfrom->PushMessage(NetMsgType::MNGOVERNANCESYNC, nHash);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CGovernanceManager::AcceptObjectMessage(const uint256& nHash)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
return AcceptMessage(nHash, setRequestedObjects);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CGovernanceManager::AcceptVoteMessage(const uint256& nHash)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
return AcceptMessage(nHash, setRequestedVotes);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CGovernanceManager::AcceptMessage(const uint256& nHash, hash_s_t& setHash)
|
|
|
|
{
|
|
|
|
hash_s_it it = setHash.find(nHash);
|
|
|
|
if(it == setHash.end()) {
|
|
|
|
// We never requested this
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Only accept one response
|
|
|
|
setHash.erase(it);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceManager::RebuildIndexes()
|
|
|
|
{
|
|
|
|
mapVoteToObject.Clear();
|
|
|
|
for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) {
|
|
|
|
CGovernanceObject& govobj = it->second;
|
|
|
|
std::vector<CGovernanceVote> vecVotes = govobj.GetVoteFile().GetVotes();
|
|
|
|
for(size_t i = 0; i < vecVotes.size(); ++i) {
|
|
|
|
mapVoteToObject.Insert(vecVotes[i].GetHash(), &govobj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int CGovernanceManager::GetMasternodeIndex(const CTxIn& masternodeVin)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
bool fIndexRebuilt = false;
|
|
|
|
int nMNIndex = mnodeman.GetMasternodeIndex(masternodeVin, fIndexRebuilt);
|
|
|
|
while(fIndexRebuilt) {
|
|
|
|
RebuildVoteMaps();
|
|
|
|
nMNIndex = mnodeman.GetMasternodeIndex(masternodeVin, fIndexRebuilt);
|
|
|
|
}
|
|
|
|
return nMNIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceManager::RebuildVoteMaps()
|
|
|
|
{
|
|
|
|
for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) {
|
|
|
|
it->second.RebuildVoteMap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-05 17:13:30 +01:00
|
|
|
void CGovernanceManager::AddCachedTriggers()
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
|
|
|
|
for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) {
|
|
|
|
CGovernanceObject& govobj = it->second;
|
|
|
|
|
|
|
|
if(govobj.nObjectType != GOVERNANCE_OBJECT_TRIGGER) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
triggerman.AddNewTrigger(govobj.GetHash());
|
2016-11-13 18:52:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-28 15:21:50 +01:00
|
|
|
void CGovernanceManager::InitOnLoad()
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-11-30 02:33:47 +01:00
|
|
|
int64_t nStart = GetTimeMillis();
|
|
|
|
LogPrintf("Preparing masternode indexes and governance triggers...\n");
|
2016-11-28 15:21:50 +01:00
|
|
|
RebuildIndexes();
|
|
|
|
AddCachedTriggers();
|
2016-11-30 02:33:47 +01:00
|
|
|
LogPrintf("Masternode indexes and governance triggers prepared %dms\n", GetTimeMillis() - nStart);
|
|
|
|
LogPrintf(" %s\n", ToString());
|
2016-11-28 15:21:50 +01:00
|
|
|
}
|
|
|
|
|
2016-04-09 22:55:52 +02:00
|
|
|
std::string CGovernanceManager::ToString() const
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
|
|
|
std::ostringstream info;
|
|
|
|
|
2016-05-19 23:08:57 +02:00
|
|
|
info << "Governance Objects: " << (int)mapObjects.size() <<
|
2016-11-30 02:33:47 +01:00
|
|
|
" (Seen: " << (int)mapSeenGovernanceObjects.size() <<
|
|
|
|
"), Vote Count: " << (int)mapVoteToObject.GetSize();
|
2016-04-09 21:57:53 +02:00
|
|
|
|
|
|
|
return info.str();
|
|
|
|
}
|
|
|
|
|
2016-04-09 22:55:52 +02:00
|
|
|
void CGovernanceManager::UpdatedBlockTip(const CBlockIndex *pindex)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
// Note this gets called from ActivateBestChain without cs_main being held
|
|
|
|
// so it should be safe to lock our mutex here without risking a deadlock
|
2016-09-12 09:40:00 +02:00
|
|
|
// On the other hand it should be safe for us to access pindex without holding a lock
|
2016-08-17 09:08:25 +02:00
|
|
|
// on cs_main because the CBlockIndex objects are dynamically allocated and
|
|
|
|
// presumably never deleted.
|
2016-11-12 12:14:50 +01:00
|
|
|
if(!pindex) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
2016-04-09 21:57:53 +02:00
|
|
|
pCurrentBlockIndex = pindex;
|
2016-08-17 09:08:25 +02:00
|
|
|
nCachedBlockHeight = pCurrentBlockIndex->nHeight;
|
2016-11-05 17:13:30 +01:00
|
|
|
LogPrint("gobject", "CGovernanceManager::UpdatedBlockTip pCurrentBlockIndex->nHeight: %d\n", pCurrentBlockIndex->nHeight);
|
2016-08-17 09:08:25 +02:00
|
|
|
|
|
|
|
// TO REPROCESS OBJECTS WE SHOULD BE SYNCED
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-29 21:11:34 +02:00
|
|
|
if(!fLiteMode && masternodeSync.IsSynced())
|
2016-04-09 21:57:53 +02:00
|
|
|
NewBlock();
|
|
|
|
}
|