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-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-09-05 01:44:10 +02:00
|
|
|
CGovernanceManager::CGovernanceManager()
|
|
|
|
: mapCollateral(),
|
|
|
|
pCurrentBlockIndex(NULL),
|
|
|
|
nTimeLastDiff(0),
|
|
|
|
nCachedBlockHeight(0),
|
|
|
|
mapObjects(),
|
|
|
|
mapSeenGovernanceObjects(),
|
|
|
|
mapSeenVotes(),
|
|
|
|
mapOrphanVotes(),
|
2016-10-17 20:54:28 +02:00
|
|
|
mapVotesByHash(),
|
|
|
|
mapVotesByType(),
|
2016-09-05 01:44:10 +02:00
|
|
|
mapLastMasternodeTrigger(),
|
|
|
|
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-09-12 09:40:00 +02:00
|
|
|
bool CGovernanceManager::HaveVoteForHash(uint256 nHash) {
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
|
|
|
return (mapVotesByHash.count(nHash) == 1);
|
|
|
|
}
|
|
|
|
|
2016-09-12 09:40:00 +02: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-09-12 09:40:00 +02:00
|
|
|
bool CGovernanceManager::SerializeVoteForHash(uint256 nHash, CDataStream& ss) {
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
|
|
|
vote_m_it it = mapVotesByHash.find(nHash);
|
2016-09-12 09:40:00 +02:00
|
|
|
if (it == mapVotesByHash.end()) {
|
2016-08-17 09:08:25 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ss << it->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceManager::AddSeenGovernanceObject(uint256 nHash, int status)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
mapSeenGovernanceObjects[nHash] = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceManager::AddSeenVote(uint256 nHash, int status)
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
mapSeenVotes[nHash] = status;
|
|
|
|
}
|
|
|
|
|
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-09-08 13:40:19 +02:00
|
|
|
LOCK(governance.cs);
|
2016-06-08 08:57:16 +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-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
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) {
|
|
|
|
LogPrintf("CGovernanceManager::ProcessMessage MNGOVERNANCEOBJECT -- pCurrentBlockIndex is NULL\n");
|
|
|
|
return;
|
|
|
|
}
|
2016-06-08 08:57:16 +02:00
|
|
|
|
|
|
|
CGovernanceObject govobj;
|
|
|
|
vRecv >> govobj;
|
|
|
|
|
|
|
|
if(mapSeenGovernanceObjects.count(govobj.GetHash())){
|
|
|
|
// TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE?
|
|
|
|
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
|
|
|
|
|
|
|
if(!govobj.IsValidLocally(pCurrentBlockIndex, strError, true)) {
|
2016-09-12 09:40:00 +02:00
|
|
|
mapSeenGovernanceObjects.insert(std::make_pair(govobj.GetHash(), 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))
|
|
|
|
{
|
|
|
|
LogPrintf("MNGOVERNANCEOBJECT -- %s new\n", govobj.GetHash().ToString());
|
|
|
|
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-10-22 18:52:14 +02:00
|
|
|
mapSeenGovernanceObjects.insert(std::make_pair(govobj.GetHash(), SEEN_OBJECT_IS_VALID));
|
2016-06-08 08:57:16 +02:00
|
|
|
masternodeSync.AddedBudgetItem(govobj.GetHash());
|
|
|
|
|
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-06-08 08:57:16 +02:00
|
|
|
CheckOrphanVotes();
|
|
|
|
}
|
|
|
|
|
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()) {
|
|
|
|
LogPrint("gobject", "CGovernanceManager::ProcessMessage MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n");
|
|
|
|
return;
|
|
|
|
}
|
2016-08-29 21:11:34 +02:00
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
CGovernanceVote vote;
|
|
|
|
vRecv >> vote;
|
2016-09-08 13:40:19 +02:00
|
|
|
//vote.fValid = true;
|
2016-06-08 08:57:16 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF WE'VE SEEN THIS OBJECT THEN SKIP
|
|
|
|
|
2016-09-29 22:23:02 +02:00
|
|
|
if(mapSeenVotes.count(vote.GetHash())) return;
|
2016-06-08 08:57:16 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// FIND THE MASTERNODE OF THE VOTER
|
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
if(!mnodeman.Has(vote.GetVinMasternode())) {
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- unknown masternode - vin: %s\n", vote.GetVinMasternode().ToString());
|
2016-09-08 13:40:19 +02:00
|
|
|
mnodeman.AskForMN(pfrom, vote.GetVinMasternode());
|
2016-06-08 08:57:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// CHECK LOCAL VALIDITY AGAINST BLOCKCHAIN, TIME DATA, ETC
|
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
if(!vote.IsValid(true)){
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("MNGOVERNANCEOBJECTVOTE -- signature invalid\n");
|
2016-06-08 08:57:16 +02:00
|
|
|
if(masternodeSync.IsSynced()) Misbehaving(pfrom->GetId(), 20);
|
|
|
|
// it could just be a non-synced masternode
|
2016-09-08 13:40:19 +02:00
|
|
|
mnodeman.AskForMN(pfrom, vote.GetVinMasternode());
|
2016-09-12 09:40:00 +02:00
|
|
|
mapSeenVotes.insert(std::make_pair(vote.GetHash(), SEEN_OBJECT_ERROR_INVALID));
|
2016-06-08 08:57:16 +02:00
|
|
|
return;
|
|
|
|
} else {
|
2016-09-12 09:40:00 +02:00
|
|
|
mapSeenVotes.insert(std::make_pair(vote.GetHash(), SEEN_OBJECT_IS_VALID));
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF EVERYTHING CHECKS OUT, UPDATE THE GOVERNANCE MANAGER
|
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
std::string strError = "";
|
2016-08-17 09:08:25 +02:00
|
|
|
if(AddOrUpdateVote(vote, pfrom, strError)) {
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- %s new\n", vote.GetHash().ToString());
|
2016-06-08 08:57:16 +02:00
|
|
|
vote.Relay();
|
|
|
|
masternodeSync.AddedBudgetItem(vote.GetHash());
|
2016-10-17 20:54:28 +02:00
|
|
|
mnodeman.AddGovernanceVote(vote.GetVinMasternode(), vote.GetParentHash());
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-04-09 22:55:52 +02:00
|
|
|
void CGovernanceManager::CheckOrphanVotes()
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
|
|
|
|
std::string strError = "";
|
2016-09-12 09:40:00 +02:00
|
|
|
vote_m_it it1 = mapOrphanVotes.begin();
|
2016-05-23 19:58:39 +02:00
|
|
|
while(it1 != mapOrphanVotes.end()){
|
2016-08-17 09:08:25 +02:00
|
|
|
if(AddOrUpdateVote(((*it1).second), NULL, strError)){
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceManager::CheckOrphanVotes -- Governance object is known, activating and removing orphan vote\n");
|
2016-05-23 19:58:39 +02:00
|
|
|
mapOrphanVotes.erase(it1++);
|
2016-04-09 21:57:53 +02:00
|
|
|
} else {
|
|
|
|
++it1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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; );
|
|
|
|
|
|
|
|
// 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-05-28 12:31:44 +02:00
|
|
|
if(mapObjects.count(govobj.GetHash())) {
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceManager::AddGovernanceObject -- already have governance object - %s\n", strError);
|
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-09-12 09:40:00 +02:00
|
|
|
mapObjects.insert(std::make_pair(govobj.GetHash(), 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-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; );
|
|
|
|
triggerman.AddNewTrigger(govobj.GetHash());
|
|
|
|
DBG( cout << "CGovernanceManager::AddGovernanceObject After AddNewTrigger" << endl; );
|
2016-10-17 20:54:28 +02:00
|
|
|
break;
|
|
|
|
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-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;
|
|
|
|
}
|
|
|
|
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-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
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
count_m_t mapDirtyObjects;
|
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-09-12 09:40:00 +02:00
|
|
|
if(pObj->fDirtyCache) {
|
|
|
|
mapDirtyObjects.insert(std::make_pair((*it).first, 1));
|
|
|
|
|
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-09-12 09:40:00 +02:00
|
|
|
if(pObj->fCachedDelete || pObj->fExpired) {
|
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-08-17 09:08:25 +02:00
|
|
|
mapObjects.erase(it++);
|
|
|
|
} else {
|
|
|
|
++it;
|
|
|
|
}
|
2016-05-23 23:39:10 +02:00
|
|
|
}
|
2016-06-10 07:16:32 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// CHECK EACH GOVERNANCE OBJECTS VALIDITY (CPU HEAVY)
|
|
|
|
|
|
|
|
// 12.1 todo - compile issues
|
|
|
|
|
|
|
|
// std::map<uint256, CBudgetVote>::iterator it = mapVotesByHash.begin();
|
|
|
|
// while(it != mapVotes.end()) {
|
|
|
|
|
|
|
|
// // ONLY UPDATE THE DIRTY OBJECTS!
|
|
|
|
|
|
|
|
// if(mapDirtyObjects.count((*it).first))
|
|
|
|
// {
|
|
|
|
// (*it).second.fValid = (*it).second.IsValid(true);
|
|
|
|
// ++it;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
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-08-17 09:08:25 +02:00
|
|
|
std::vector<CGovernanceVote*> CGovernanceManager::GetMatchingVotes(const uint256& nParentHash)
|
|
|
|
{
|
|
|
|
std::vector<CGovernanceVote*> vecResult;
|
|
|
|
|
|
|
|
// LOOP THROUGH ALL VOTES AND FIND THOSE MATCHING USER HASH
|
|
|
|
|
2016-10-17 20:56:27 +02:00
|
|
|
vote_m_it it2 = mapVotesByType.begin();
|
|
|
|
while(it2 != mapVotesByType.end()) {
|
2016-09-12 09:40:00 +02:00
|
|
|
if((*it2).second.GetParentHash() == nParentHash) {
|
2016-08-17 09:08:25 +02:00
|
|
|
vecResult.push_back(&(*it2).second);
|
|
|
|
}
|
2016-09-12 09:40:00 +02:00
|
|
|
++it2;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return vecResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
if((*it).second.nTime < nMoreThanTime) {
|
|
|
|
++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);
|
|
|
|
return (UintToArith256(left.first->nCollateralHash) > UintToArith256(right.first->nCollateralHash));
|
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();
|
|
|
|
while(it != mapAskedForGovernanceObject.end()){
|
2016-04-09 21:57:53 +02:00
|
|
|
if((*it).second > GetTime() - (60*60*24)){
|
|
|
|
++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-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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int nInvCount = 0;
|
|
|
|
|
2016-05-24 20:11:59 +02:00
|
|
|
// SYNC GOVERNANCE OBJECTS WITH OTHER CLIENT
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-09-12 09:40:00 +02:00
|
|
|
object_m_it it1 = mapObjects.begin();
|
2016-08-17 09:08:25 +02:00
|
|
|
while(it1 != mapObjects.end()) {
|
|
|
|
uint256 h = (*it1).first;
|
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
if((*it1).second.fCachedValid && (nProp == uint256() || h == nProp)) {
|
2016-08-17 09:08:25 +02:00
|
|
|
// Push the inventory budget proposal message over to the other client
|
|
|
|
pfrom->PushInventory(CInv(MSG_GOVERNANCE_OBJECT, h));
|
|
|
|
nInvCount++;
|
|
|
|
}
|
|
|
|
++it1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SYNC OUR GOVERNANCE OBJECT VOTES WITH THEIR GOVERNANCE OBJECT VOTES
|
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
vote_m_it it2 = mapVotesByHash.begin();
|
2016-08-17 09:08:25 +02:00
|
|
|
while(it2 != mapVotesByHash.end()) {
|
2016-11-05 17:13:30 +01:00
|
|
|
CGovernanceVote& vote = it2->second;
|
|
|
|
if(!vote.IsValid(true)) {
|
|
|
|
// Don't relay votes that are now invalid (ie. missing MN) to avoid being banned
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pfrom->PushInventory(CInv(MSG_GOVERNANCE_OBJECT_VOTE, (*it2).first));
|
|
|
|
nInvCount++;
|
|
|
|
++it2;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
2016-04-14 00:41:40 +02:00
|
|
|
}
|
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
pfrom->PushMessage(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_GOVOBJ, nInvCount);
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceManager::Sync -- sent %d items\n", nInvCount);
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
void CGovernanceManager::SyncParentObjectByVote(CNode* pfrom, const CGovernanceVote& vote)
|
|
|
|
{
|
2016-09-08 13:40:19 +02:00
|
|
|
if(!mapAskedForGovernanceObject.count(vote.GetParentHash())){
|
|
|
|
pfrom->PushMessage(NetMsgType::MNGOVERNANCESYNC, vote.GetParentHash());
|
|
|
|
mapAskedForGovernanceObject[vote.GetParentHash()] = GetTime();
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
bool CGovernanceManager::AddOrUpdateVote(const CGovernanceVote& vote, CNode* pfrom, std::string& strError)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
// MAKE SURE WE HAVE THE PARENT OBJECT THE VOTE IS FOR
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
bool syncparent = false;
|
|
|
|
uint256 votehash;
|
|
|
|
{
|
|
|
|
LOCK(cs);
|
2016-09-08 13:40:19 +02:00
|
|
|
if(!mapObjects.count(vote.GetParentHash())) {
|
2016-10-07 18:15:39 +02:00
|
|
|
if(pfrom) {
|
|
|
|
// only ask for missing items after our syncing process is complete --
|
|
|
|
// otherwise we'll think a full sync succeeded when they return a result
|
|
|
|
if(!masternodeSync.IsSynced()) return false;
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-10-07 18:15:39 +02:00
|
|
|
// ADD THE VOTE AS AN ORPHAN, TO BE USED UPON RECEIVAL OF THE PARENT OBJECT
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceManager::AddOrUpdateVote -- Unknown object %s, asking for source\n", vote.GetParentHash().ToString());
|
2016-10-07 18:15:39 +02:00
|
|
|
mapOrphanVotes[vote.GetParentHash()] = vote;
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-10-07 18:15:39 +02:00
|
|
|
// ASK FOR THIS VOTES PARENT SPECIFICALLY FROM THIS USER (THEY SHOULD HAVE IT, NO?)
|
2016-08-17 09:08:25 +02:00
|
|
|
|
2016-10-07 18:15:39 +02:00
|
|
|
if(!mapAskedForGovernanceObject.count(vote.GetParentHash())){
|
|
|
|
syncparent = true;
|
|
|
|
votehash = vote.GetParentHash();
|
2016-10-22 18:52:14 +02:00
|
|
|
mapAskedForGovernanceObject[vote.GetParentHash()] = GetTime();
|
2016-10-07 18:15:39 +02:00
|
|
|
} else {
|
|
|
|
strError = "Governance object not found! Sync message has been already pushed.";
|
|
|
|
return false;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
2016-10-07 18:15:39 +02:00
|
|
|
}
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// Need to keep this out of the locked section
|
2016-09-12 09:40:00 +02:00
|
|
|
if(syncparent) {
|
2016-08-17 09:08:25 +02:00
|
|
|
pfrom->PushMessage(NetMsgType::MNGOVERNANCESYNC, votehash);
|
2016-10-07 18:15:39 +02:00
|
|
|
strError = "Governance object not found! Sync message was pushed.";
|
|
|
|
return false;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// Reestablish lock
|
2016-04-14 00:41:40 +02:00
|
|
|
LOCK(cs);
|
2016-06-08 08:57:16 +02:00
|
|
|
// GET DETERMINISTIC HASH WHICH COLLIDES ON MASTERNODE-VIN/GOVOBJ-HASH/VOTE-SIGNAL
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
uint256 nTypeHash = vote.GetTypeHash();
|
|
|
|
uint256 nHash = vote.GetHash();
|
2016-04-14 03:52:26 +02:00
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
// LOOK FOR PREVIOUS VOTES BY THIS SPECIFIC MASTERNODE FOR THIS SPECIFIC SIGNAL
|
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
vote_m_it it = mapVotesByType.find(nTypeHash);
|
|
|
|
if(it != mapVotesByType.end()) {
|
|
|
|
if(it->second.GetTimestamp() > vote.GetTimestamp()) {
|
2016-06-08 08:57:16 +02:00
|
|
|
strError = strprintf("new vote older than existing vote - %s", nTypeHash.ToString());
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "CGovernanceObject::AddOrUpdateVote -- %s\n", strError);
|
2016-05-19 23:08:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-09-08 13:40:19 +02:00
|
|
|
if(vote.GetTimestamp() - it->second.GetTimestamp() < GOVERNANCE_UPDATE_MIN) {
|
|
|
|
strError = strprintf("time between votes is too soon - %s - %lli", nTypeHash.ToString(), vote.GetTimestamp() - it->second.GetTimestamp());
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "CGovernanceObject::AddOrUpdateVote -- %s\n", strError);
|
2016-05-19 23:08:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-04-14 00:41:40 +02:00
|
|
|
|
2016-06-08 08:57:16 +02:00
|
|
|
// UPDATE TO NEWEST VOTE
|
2016-08-17 09:08:25 +02:00
|
|
|
{
|
2016-09-12 09:40:00 +02:00
|
|
|
mapVotesByHash[nHash] = vote;
|
2016-08-17 09:08:25 +02:00
|
|
|
mapVotesByType[nTypeHash] = vote;
|
|
|
|
}
|
2016-06-08 08:57:16 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// SET CACHE AS DIRTY / WILL BE UPDATED NEXT BLOCK
|
2016-06-10 07:16:32 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
CGovernanceObject* pGovObj = FindGovernanceObject(vote.GetParentHash());
|
|
|
|
if(pGovObj)
|
|
|
|
{
|
|
|
|
pGovObj->fDirtyCache = true;
|
|
|
|
UpdateCachesAndClean();
|
2016-10-17 20:54:28 +02:00
|
|
|
if(pGovObj->GetObjectType() == GOVERNANCE_OBJECT_WATCHDOG) {
|
|
|
|
mnodeman.UpdateWatchdogVoteTime(vote.GetVinMasternode());
|
|
|
|
}
|
2016-08-17 09:08:25 +02:00
|
|
|
} else {
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceObject::AddOrUpdateVote -- Governance object not found! Can't update fDirtyCache - %s\n", vote.GetParentHash().ToString());
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
2016-06-10 07:16:32 +02:00
|
|
|
|
2016-04-14 00:41:40 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
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-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-04-16 19:19:17 +02:00
|
|
|
CGovernanceObject::CGovernanceObject()
|
2016-10-17 20:54:28 +02:00
|
|
|
: cs(),
|
|
|
|
nHashParent(),
|
|
|
|
nRevision(0),
|
|
|
|
nTime(0),
|
|
|
|
nCollateralHash(),
|
|
|
|
strData(),
|
|
|
|
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
|
|
|
vinMasternode(),
|
|
|
|
vchSig(),
|
|
|
|
fCachedLocalValidity(false),
|
|
|
|
strLocalValidityError(),
|
|
|
|
fCachedFunding(false),
|
|
|
|
fCachedValid(true),
|
|
|
|
fCachedDelete(false),
|
|
|
|
fCachedEndorsed(false),
|
|
|
|
fDirtyCache(true),
|
|
|
|
fUnparsable(false),
|
|
|
|
fExpired(false)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
// PARSE JSON DATA STORAGE (STRDATA)
|
|
|
|
LoadData();
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-09-17 21:37:48 +02:00
|
|
|
CGovernanceObject::CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTimeIn, uint256 nCollateralHashIn, std::string strDataIn)
|
2016-10-17 20:54:28 +02:00
|
|
|
: cs(),
|
|
|
|
nHashParent(nHashParentIn),
|
|
|
|
nRevision(nRevisionIn),
|
|
|
|
nTime(nTimeIn),
|
|
|
|
nCollateralHash(nCollateralHashIn),
|
|
|
|
strData(strDataIn),
|
|
|
|
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
|
|
|
vinMasternode(),
|
|
|
|
vchSig(),
|
|
|
|
fCachedLocalValidity(false),
|
|
|
|
strLocalValidityError(),
|
|
|
|
fCachedFunding(false),
|
|
|
|
fCachedValid(true),
|
|
|
|
fCachedDelete(false),
|
|
|
|
fCachedEndorsed(false),
|
|
|
|
fDirtyCache(true),
|
|
|
|
fUnparsable(false),
|
|
|
|
fExpired(false)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
// PARSE JSON DATA STORAGE (STRDATA)
|
|
|
|
LoadData();
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-04-26 13:42:34 +02:00
|
|
|
CGovernanceObject::CGovernanceObject(const CGovernanceObject& other)
|
2016-10-17 20:54:28 +02:00
|
|
|
: cs(),
|
|
|
|
nHashParent(other.nHashParent),
|
|
|
|
nRevision(other.nRevision),
|
|
|
|
nTime(other.nTime),
|
|
|
|
nCollateralHash(other.nCollateralHash),
|
|
|
|
strData(other.strData),
|
|
|
|
nObjectType(other.nObjectType),
|
|
|
|
vinMasternode(other.vinMasternode),
|
|
|
|
vchSig(other.vchSig),
|
|
|
|
fCachedLocalValidity(other.fCachedLocalValidity),
|
|
|
|
strLocalValidityError(other.strLocalValidityError),
|
|
|
|
fCachedFunding(other.fCachedFunding),
|
|
|
|
fCachedValid(other.fCachedValid),
|
|
|
|
fCachedDelete(other.fCachedDelete),
|
|
|
|
fCachedEndorsed(other.fCachedEndorsed),
|
|
|
|
fDirtyCache(other.fDirtyCache),
|
|
|
|
fUnparsable(other.fUnparsable),
|
|
|
|
fExpired(other.fExpired)
|
|
|
|
{}
|
2016-04-26 13:42:34 +02:00
|
|
|
|
2016-09-15 08:49:24 +02:00
|
|
|
void CGovernanceObject::SetMasternodeInfo(const CTxIn& vin)
|
2016-09-05 01:44:10 +02:00
|
|
|
{
|
|
|
|
vinMasternode = vin;
|
|
|
|
}
|
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
bool CGovernanceObject::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
|
2016-09-05 01:44:10 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
|
|
|
|
std::string strError;
|
|
|
|
uint256 nHash = GetHash();
|
|
|
|
std::string strMessage = nHash.ToString();
|
|
|
|
|
|
|
|
if(!darkSendSigner.SignMessage(strMessage, vchSig, keyMasternode)) {
|
|
|
|
LogPrintf("CGovernanceObject::Sign -- SignMessage() failed\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
2016-09-05 01:44:10 +02:00
|
|
|
LogPrintf("CGovernanceObject::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrint("gobject", "CGovernanceObject::Sign -- pubkey id = %s, vin = %s\n",
|
|
|
|
pubKeyMasternode.GetID().ToString(), vinMasternode.prevout.ToStringShort());
|
2016-09-15 08:49:24 +02:00
|
|
|
|
|
|
|
|
2016-09-05 01:44:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
bool CGovernanceObject::CheckSignature(CPubKey& pubKeyMasternode)
|
2016-09-05 01:44:10 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
|
|
|
std::string strError;
|
|
|
|
uint256 nHash = GetHash();
|
|
|
|
std::string strMessage = nHash.ToString();
|
|
|
|
|
2016-10-22 18:52:14 +02:00
|
|
|
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
2016-09-05 01:44:10 +02:00
|
|
|
LogPrintf("CGovernance::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
int CGovernanceObject::GetObjectType()
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-08-17 09:08:25 +02:00
|
|
|
return nObjectType;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CGovernanceObject::GetObjectSubtype()
|
|
|
|
{
|
|
|
|
// todo - 12.1
|
|
|
|
// - detect subtype from strData json, obj["subtype"]
|
|
|
|
|
|
|
|
if(nObjectType == GOVERNANCE_OBJECT_TRIGGER) return TRIGGER_SUPERBLOCK;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint256 CGovernanceObject::GetHash()
|
|
|
|
{
|
|
|
|
// CREATE HASH OF ALL IMPORTANT PIECES OF DATA
|
|
|
|
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
|
|
ss << nHashParent;
|
|
|
|
ss << nRevision;
|
|
|
|
ss << nTime;
|
|
|
|
ss << strData;
|
|
|
|
// fee_tx is left out on purpose
|
|
|
|
uint256 h1 = ss.GetHash();
|
|
|
|
|
2016-09-17 21:37:48 +02:00
|
|
|
DBG( printf("CGovernanceObject::GetHash %i %li %s\n", nRevision, nTime, strData.c_str()); );
|
2016-08-17 09:08:25 +02:00
|
|
|
|
|
|
|
return h1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Return the actual object from the strData JSON structure.
|
|
|
|
|
|
|
|
Returns an empty object on error.
|
|
|
|
*/
|
|
|
|
UniValue CGovernanceObject::GetJSONObject()
|
|
|
|
{
|
|
|
|
UniValue obj(UniValue::VOBJ);
|
2016-09-12 09:40:00 +02:00
|
|
|
if(strData.empty()) {
|
2016-08-17 09:08:25 +02:00
|
|
|
return obj;
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
UniValue objResult(UniValue::VOBJ);
|
2016-09-08 13:40:19 +02:00
|
|
|
GetData(objResult);
|
|
|
|
|
|
|
|
std::vector<UniValue> arr1 = objResult.getValues();
|
|
|
|
std::vector<UniValue> arr2 = arr1.at( 0 ).getValues();
|
|
|
|
obj = arr2.at( 1 );
|
2016-04-09 21:57:53 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* LoadData
|
|
|
|
* --------------------------------------------------------
|
2016-09-12 09:40:00 +02:00
|
|
|
*
|
|
|
|
* Attempt to load data from strData
|
|
|
|
*
|
2016-08-17 09:08:25 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
void CGovernanceObject::LoadData()
|
|
|
|
{
|
|
|
|
// todo : 12.1 - resolved
|
|
|
|
//return;
|
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
if(strData.empty()) {
|
2016-08-17 09:08:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-29 00:08:27 +02:00
|
|
|
try {
|
2016-09-08 13:40:19 +02:00
|
|
|
// ATTEMPT TO LOAD JSON STRING FROM STRDATA
|
|
|
|
UniValue objResult(UniValue::VOBJ);
|
|
|
|
GetData(objResult);
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
DBG( cout << "CGovernanceObject::LoadData strData = "
|
|
|
|
<< GetDataAsString()
|
|
|
|
<< endl; );
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
UniValue obj = GetJSONObject();
|
|
|
|
nObjectType = obj["type"].get_int();
|
|
|
|
}
|
2016-09-12 09:40:00 +02:00
|
|
|
catch(std::exception& e) {
|
2016-10-17 20:54:28 +02:00
|
|
|
fUnparsable = true;
|
2016-08-29 00:08:27 +02:00
|
|
|
std::ostringstream ostr;
|
|
|
|
ostr << "CGovernanceObject::LoadData Error parsing JSON"
|
|
|
|
<< ", e.what() = " << e.what();
|
|
|
|
DBG( cout << ostr.str() << endl; );
|
|
|
|
LogPrintf( ostr.str().c_str() );
|
|
|
|
return;
|
|
|
|
}
|
2016-09-12 09:40:00 +02:00
|
|
|
catch(...) {
|
2016-10-17 20:54:28 +02:00
|
|
|
fUnparsable = true;
|
2016-08-29 00:08:27 +02:00
|
|
|
std::ostringstream ostr;
|
|
|
|
ostr << "CGovernanceObject::LoadData Unknown Error parsing JSON";
|
|
|
|
DBG( cout << ostr.str() << endl; );
|
|
|
|
LogPrintf( ostr.str().c_str() );
|
2016-08-17 09:08:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GetData - Example usage:
|
|
|
|
* --------------------------------------------------------
|
2016-09-12 09:40:00 +02:00
|
|
|
*
|
2016-08-17 09:08:25 +02:00
|
|
|
* Decode governance object data into UniValue(VOBJ)
|
2016-09-12 09:40:00 +02:00
|
|
|
*
|
2016-08-17 09:08:25 +02:00
|
|
|
*/
|
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
void CGovernanceObject::GetData(UniValue& objResult)
|
2016-08-17 09:08:25 +02:00
|
|
|
{
|
2016-09-08 13:40:19 +02:00
|
|
|
UniValue o(UniValue::VOBJ);
|
|
|
|
std::string s = GetDataAsString();
|
|
|
|
o.read(s);
|
|
|
|
objResult = o;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GetData - As
|
|
|
|
* --------------------------------------------------------
|
2016-09-12 09:40:00 +02:00
|
|
|
*
|
2016-08-17 09:08:25 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
std::string CGovernanceObject::GetDataAsHex()
|
|
|
|
{
|
|
|
|
return strData;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CGovernanceObject::GetDataAsString()
|
|
|
|
{
|
2016-09-12 09:40:00 +02:00
|
|
|
std::vector<unsigned char> v = ParseHex(strData);
|
|
|
|
std::string s(v.begin(), v.end());
|
2016-08-17 09:08:25 +02:00
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceObject::UpdateLocalValidity(const CBlockIndex *pCurrentBlockIndex)
|
|
|
|
{
|
|
|
|
// THIS DOES NOT CHECK COLLATERAL, THIS IS CHECKED UPON ORIGINAL ARRIVAL
|
|
|
|
fCachedLocalValidity = IsValidLocally(pCurrentBlockIndex, strLocalValidityError, false);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
bool CGovernanceObject::IsValidLocally(const CBlockIndex* pindex, std::string& strError, bool fCheckCollateral)
|
|
|
|
{
|
2016-04-09 21:57:53 +02:00
|
|
|
if(!pindex) {
|
|
|
|
strError = "Tip is NULL";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
if(fUnparsable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-29 22:29:29 +02:00
|
|
|
switch(nObjectType) {
|
|
|
|
case GOVERNANCE_OBJECT_PROPOSAL:
|
|
|
|
case GOVERNANCE_OBJECT_TRIGGER:
|
2016-10-17 20:54:28 +02:00
|
|
|
case GOVERNANCE_OBJECT_WATCHDOG:
|
2016-09-29 22:29:29 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
strError = strprintf("Invalid object type %d", nObjectType);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// IF ABSOLUTE NO COUNT (NO-YES VALID VOTES) IS MORE THAN 10% OF THE NETWORK MASTERNODES, OBJ IS INVALID
|
|
|
|
|
2016-09-28 22:03:54 +02:00
|
|
|
if(GetAbsoluteNoCount(VOTE_SIGNAL_VALID) > mnodeman.CountEnabled(MIN_GOVERNANCE_PEER_PROTO_VERSION)/10) {
|
2016-09-05 01:44:10 +02:00
|
|
|
strError = "Automated removal";
|
|
|
|
return false;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// CHECK COLLATERAL IF REQUIRED (HIGH CPU USAGE)
|
|
|
|
|
2016-10-17 20:54:28 +02:00
|
|
|
if(fCheckCollateral) {
|
|
|
|
if((nObjectType == GOVERNANCE_OBJECT_TRIGGER) || (nObjectType == GOVERNANCE_OBJECT_WATCHDOG)) {
|
2016-10-22 18:52:14 +02:00
|
|
|
std::string strOutpoint = vinMasternode.prevout.ToStringShort();
|
2016-10-17 20:54:28 +02:00
|
|
|
masternode_info_t infoMn = mnodeman.GetMasternodeInfo(vinMasternode);
|
|
|
|
if(!infoMn.fInfoValid) {
|
2016-10-22 18:52:14 +02:00
|
|
|
strError = "Masternode not found: " + strOutpoint;
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we have a valid MN signature
|
2016-10-17 20:54:28 +02:00
|
|
|
if(!CheckSignature(infoMn.pubKeyMasternode)) {
|
2016-10-22 18:52:14 +02:00
|
|
|
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey id = " + infoMn.pubKeyMasternode.GetID().ToString();
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-15 08:49:24 +02:00
|
|
|
// Only perform rate check if we are synced because during syncing it is expected
|
|
|
|
// that objects will be seen in rapid succession
|
|
|
|
if(masternodeSync.IsSynced()) {
|
2016-10-17 20:54:28 +02:00
|
|
|
if(!governance.MasternodeRateCheck(vinMasternode, nObjectType)) {
|
2016-10-22 18:52:14 +02:00
|
|
|
strError = "Masternode attempting to create too many objects: " + strOutpoint;
|
2016-09-15 08:49:24 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-09-05 01:44:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!IsCollateralValid(strError)) {
|
2016-05-13 18:03:01 +02:00
|
|
|
// strError set in IsCollateralValid
|
2016-08-17 09:08:25 +02:00
|
|
|
if(strError == "") strError = "Collateral is invalid";
|
2016-04-09 21:57:53 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-08-17 09:08:25 +02:00
|
|
|
TODO
|
|
|
|
|
|
|
|
- There might be an issue with multisig in the coinbase on mainnet, we will add support for it in a future release.
|
|
|
|
- Post 12.2+ (test multisig coinbase transaction)
|
2016-04-09 21:57:53 +02:00
|
|
|
*/
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// 12.1 - todo - compile error
|
|
|
|
// if(address.IsPayToScriptHash()) {
|
|
|
|
// strError = "Governance system - multisig is not currently supported";
|
2016-04-14 23:28:33 +02:00
|
|
|
// return false;
|
|
|
|
// }
|
2016-04-09 21:57:53 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-05 01:44:10 +02:00
|
|
|
CAmount CGovernanceObject::GetMinCollateralFee()
|
|
|
|
{
|
|
|
|
// Only 1 type has a fee for the moment but switch statement allows for future object types
|
2016-09-12 09:40:00 +02:00
|
|
|
switch(nObjectType) {
|
2016-09-29 22:29:29 +02:00
|
|
|
case GOVERNANCE_OBJECT_PROPOSAL: return GOVERNANCE_PROPOSAL_FEE_TX;
|
|
|
|
case GOVERNANCE_OBJECT_TRIGGER: return 0;
|
2016-10-17 20:54:28 +02:00
|
|
|
case GOVERNANCE_OBJECT_WATCHDOG: return 0;
|
2016-09-29 22:29:29 +02:00
|
|
|
default: return MAX_MONEY;
|
2016-09-05 01:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CGovernanceObject::IsCollateralValid(std::string& strError)
|
|
|
|
{
|
2016-09-29 22:29:29 +02:00
|
|
|
strError = "";
|
|
|
|
CAmount nMinFee = GetMinCollateralFee();
|
2016-09-05 01:44:10 +02:00
|
|
|
uint256 nExpectedHash = GetHash();
|
|
|
|
|
|
|
|
CTransaction txCollateral;
|
|
|
|
uint256 nBlockHash;
|
|
|
|
|
2016-09-12 09:40:00 +02:00
|
|
|
// RETRIEVE TRANSACTION IN QUESTION
|
2016-09-05 01:44:10 +02:00
|
|
|
|
|
|
|
if(!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash, true)){
|
|
|
|
strError = strprintf("Can't find collateral tx %s", txCollateral.ToString());
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(txCollateral.vout.size() < 1) {
|
|
|
|
strError = strprintf("tx vout size less than 1 | %d", txCollateral.vout.size());
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// LOOK FOR SPECIALIZED GOVERNANCE SCRIPT (PROOF OF BURN)
|
|
|
|
|
|
|
|
CScript findScript;
|
|
|
|
findScript << OP_RETURN << ToByteVector(nExpectedHash);
|
|
|
|
|
|
|
|
DBG( cout << "IsCollateralValid txCollateral.vout.size() = " << txCollateral.vout.size() << endl; );
|
|
|
|
|
|
|
|
DBG( cout << "IsCollateralValid: findScript = " << ScriptToAsmStr( findScript, false ) << endl; );
|
|
|
|
|
|
|
|
DBG( cout << "IsCollateralValid: nMinFee = " << nMinFee << endl; );
|
|
|
|
|
|
|
|
|
|
|
|
bool foundOpReturn = false;
|
|
|
|
BOOST_FOREACH(const CTxOut o, txCollateral.vout) {
|
2016-09-12 09:40:00 +02:00
|
|
|
DBG( cout << "IsCollateralValid txout : " << o.ToString()
|
2016-09-05 01:44:10 +02:00
|
|
|
<< ", o.nValue = " << o.nValue
|
|
|
|
<< ", o.scriptPubKey = " << ScriptToAsmStr( o.scriptPubKey, false )
|
|
|
|
<< endl; );
|
|
|
|
if(!o.scriptPubKey.IsNormalPaymentScript() && !o.scriptPubKey.IsUnspendable()){
|
|
|
|
strError = strprintf("Invalid Script %s", txCollateral.ToString());
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(o.scriptPubKey == findScript && o.nValue >= nMinFee) {
|
|
|
|
DBG( cout << "IsCollateralValid foundOpReturn = true" << endl; );
|
|
|
|
foundOpReturn = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DBG( cout << "IsCollateralValid No match, continuing" << endl; );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!foundOpReturn){
|
|
|
|
strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral.ToString());
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// GET CONFIRMATIONS FOR TRANSACTION
|
|
|
|
|
|
|
|
LOCK(cs_main);
|
|
|
|
int nConfirmationsIn = GetIXConfirmations(nCollateralHash);
|
|
|
|
if (nBlockHash != uint256()) {
|
|
|
|
BlockMap::iterator mi = mapBlockIndex.find(nBlockHash);
|
|
|
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
|
|
|
CBlockIndex* pindex = (*mi).second;
|
|
|
|
if (chainActive.Contains(pindex)) {
|
|
|
|
nConfirmationsIn += chainActive.Height() - pindex->nHeight + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(nConfirmationsIn >= GOVERNANCE_FEE_CONFIRMATIONS) {
|
|
|
|
strError = "valid";
|
|
|
|
} else {
|
|
|
|
strError = strprintf("Collateral requires at least %d confirmations - %d confirmations", GOVERNANCE_FEE_CONFIRMATIONS, nConfirmationsIn);
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s - %d confirmations\n", strError, nConfirmationsIn);
|
2016-09-05 01:44:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-09-05 01:44:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-26 06:08:36 +02:00
|
|
|
/**
|
|
|
|
* Get specific vote counts for each outcome (funding, validity, etc)
|
|
|
|
*/
|
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
int CGovernanceObject::GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-09-16 17:30:33 +02:00
|
|
|
return GetYesCount(eVoteSignalIn) - GetNoCount(eVoteSignalIn);
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
int CGovernanceObject::GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn)
|
2016-06-08 08:57:16 +02:00
|
|
|
{
|
2016-09-16 17:30:33 +02:00
|
|
|
return GetNoCount(eVoteSignalIn) - GetYesCount(eVoteSignalIn);
|
2016-06-08 08:57:16 +02:00
|
|
|
}
|
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
int CGovernanceObject::GetYesCount(vote_signal_enum_t eVoteSignalIn)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-09-08 13:40:19 +02:00
|
|
|
return governance.CountMatchingVotes((*this), eVoteSignalIn, VOTE_OUTCOME_YES);
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
int CGovernanceObject::GetNoCount(vote_signal_enum_t eVoteSignalIn)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-09-08 13:40:19 +02:00
|
|
|
return governance.CountMatchingVotes((*this), eVoteSignalIn, VOTE_OUTCOME_NO);
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
int CGovernanceObject::GetAbstainCount(vote_signal_enum_t eVoteSignalIn)
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-09-08 13:40:19 +02:00
|
|
|
return governance.CountMatchingVotes((*this), eVoteSignalIn, VOTE_OUTCOME_ABSTAIN);
|
2016-04-09 21:57:53 +02:00
|
|
|
}
|
|
|
|
|
2016-04-16 19:19:17 +02:00
|
|
|
void CGovernanceObject::Relay()
|
2016-04-09 21:57:53 +02:00
|
|
|
{
|
2016-06-08 08:57:16 +02:00
|
|
|
CInv inv(MSG_GOVERNANCE_OBJECT, GetHash());
|
2016-09-28 22:03:54 +02:00
|
|
|
RelayInv(inv, PROTOCOL_VERSION);
|
2016-04-09 21:57:53 +02: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-05-23 19:53:05 +02:00
|
|
|
", Seen Budgets: " << (int)mapSeenGovernanceObjects.size() <<
|
|
|
|
", Seen Budget Votes: " << (int)mapSeenVotes.size() <<
|
2016-09-12 09:40:00 +02:00
|
|
|
", VoteByHash Count: " << (int)mapVotesByHash.size() <<
|
2016-06-08 08:57:16 +02:00
|
|
|
", VoteByType Count: " << (int)mapVotesByType.size();
|
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();
|
|
|
|
}
|
2016-04-14 20:07:59 +02:00
|
|
|
|
2016-09-08 13:40:19 +02:00
|
|
|
int CGovernanceManager::CountMatchingVotes(CGovernanceObject& govobj, vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn)
|
2016-04-14 22:01:15 +02:00
|
|
|
{
|
|
|
|
/*
|
2016-09-12 09:40:00 +02:00
|
|
|
*
|
2016-04-14 22:01:15 +02:00
|
|
|
* Count matching votes and return
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
LOCK(cs);
|
2016-05-13 18:03:01 +02:00
|
|
|
int nCount = 0;
|
2016-04-14 22:01:15 +02:00
|
|
|
|
2016-10-31 19:20:49 +01:00
|
|
|
uint256 hash = govobj.GetHash();
|
2016-10-12 17:01:38 +02:00
|
|
|
std::map<uint256, CGovernanceVote>::iterator it = mapVotesByType.begin();
|
|
|
|
while(it != mapVotesByType.end()) {
|
2016-09-08 13:40:19 +02:00
|
|
|
if(it->second.IsValid() &&
|
|
|
|
it->second.GetSignal() == eVoteSignalIn &&
|
|
|
|
it->second.GetOutcome() == eVoteOutcomeIn &&
|
2016-10-31 19:20:49 +01:00
|
|
|
it->second.GetParentHash() == hash) {
|
2016-09-08 13:40:19 +02:00
|
|
|
++nCount;
|
2016-04-14 22:01:15 +02:00
|
|
|
}
|
2016-09-08 13:40:19 +02:00
|
|
|
++it;
|
2016-04-14 22:01:15 +02:00
|
|
|
}
|
|
|
|
|
2016-05-13 18:03:01 +02:00
|
|
|
return nCount;
|
2016-08-17 09:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceObject::UpdateSentinelVariables(const CBlockIndex *pCurrentBlockIndex)
|
|
|
|
{
|
|
|
|
// CALCULATE MINIMUM SUPPORT LEVELS REQUIRED
|
|
|
|
|
|
|
|
int nMnCount = mnodeman.CountEnabled();
|
|
|
|
if(nMnCount == 0) return;
|
|
|
|
|
|
|
|
// CALCULATE THE MINUMUM VOTE COUNT REQUIRED FOR FULL SIGNAL
|
|
|
|
|
|
|
|
// todo - 12.1 - should be set to `10` after governance vote compression is implemented
|
2016-09-12 09:40:00 +02:00
|
|
|
int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nMnCount / 10);
|
2016-08-17 09:08:25 +02:00
|
|
|
// todo - 12.1 - Temporarily set to 1 for testing - reverted
|
|
|
|
//nAbsVoteReq = 1;
|
|
|
|
|
|
|
|
// SET SENTINEL FLAGS TO FALSE
|
|
|
|
|
|
|
|
fCachedFunding = false;
|
|
|
|
fCachedValid = true; //default to valid
|
|
|
|
fCachedDelete = false;
|
|
|
|
fCachedEndorsed = false;
|
|
|
|
fDirtyCache = false;
|
2016-09-12 09:40:00 +02:00
|
|
|
|
2016-08-17 09:08:25 +02:00
|
|
|
// SET SENTINEL FLAGS TO TRUE IF MIMIMUM SUPPORT LEVELS ARE REACHED
|
|
|
|
// ARE ANY OF THESE FLAGS CURRENTLY ACTIVATED?
|
|
|
|
|
|
|
|
if(GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING) >= nAbsVoteReq) fCachedFunding = true;
|
|
|
|
if(GetAbsoluteYesCount(VOTE_SIGNAL_VALID) >= nAbsVoteReq) fCachedValid = true;
|
|
|
|
if(GetAbsoluteYesCount(VOTE_SIGNAL_DELETE) >= nAbsVoteReq) fCachedDelete = true;
|
|
|
|
if(GetAbsoluteYesCount(VOTE_SIGNAL_ENDORSED) >= nAbsVoteReq) fCachedEndorsed = true;
|
|
|
|
|
|
|
|
// ARE ANY OF THE VOTING FLAGS NEGATIVELY SET BY THE NETWORK?
|
|
|
|
// THIS CAN BE CACHED, THE VOTES BEING HOT-LOADED AS NEEDED TO RECALCULATE
|
|
|
|
|
|
|
|
if(GetAbsoluteNoCount(VOTE_SIGNAL_FUNDING) >= nAbsVoteReq) fCachedFunding = false;
|
|
|
|
if(GetAbsoluteNoCount(VOTE_SIGNAL_VALID) >= nAbsVoteReq) fCachedValid = false;
|
|
|
|
if(GetAbsoluteNoCount(VOTE_SIGNAL_DELETE) >= nAbsVoteReq) fCachedDelete = false;
|
|
|
|
if(GetAbsoluteNoCount(VOTE_SIGNAL_ENDORSED) >= nAbsVoteReq) fCachedEndorsed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CGovernanceObject::swap(CGovernanceObject& first, CGovernanceObject& second) // nothrow
|
|
|
|
{
|
|
|
|
// 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.nHashParent, second.nHashParent);
|
|
|
|
swap(first.nRevision, second.nRevision);
|
|
|
|
swap(first.nTime, second.nTime);
|
|
|
|
swap(first.nCollateralHash, second.nCollateralHash);
|
|
|
|
swap(first.strData, second.strData);
|
|
|
|
swap(first.nObjectType, second.nObjectType);
|
|
|
|
|
|
|
|
// swap all cached valid flags
|
|
|
|
swap(first.fCachedFunding, second.fCachedFunding);
|
|
|
|
swap(first.fCachedValid, second.fCachedValid);
|
|
|
|
swap(first.fCachedDelete, second.fCachedDelete);
|
|
|
|
swap(first.fCachedEndorsed, second.fCachedEndorsed);
|
|
|
|
swap(first.fDirtyCache, second.fDirtyCache);
|
|
|
|
swap(first.fExpired, second.fExpired);
|
|
|
|
}
|