Implement orphan object and vote timeouts (#1166)
This commit is contained in:
parent
3e8c0062a0
commit
1b90d66ab6
@ -102,6 +102,7 @@ BITCOIN_CORE_H = \
|
||||
governance.h \
|
||||
governance-classes.h \
|
||||
governance-exceptions.h \
|
||||
governance-object.h \
|
||||
governance-vote.h \
|
||||
governance-votedb.h \
|
||||
flat-database.h \
|
||||
@ -205,6 +206,7 @@ libbitcoin_server_a_SOURCES = \
|
||||
dbwrapper.cpp \
|
||||
governance.cpp \
|
||||
governance-classes.cpp \
|
||||
governance-object.cpp \
|
||||
governance-vote.cpp \
|
||||
governance-votedb.cpp \
|
||||
main.cpp \
|
||||
|
753
src/governance-object.cpp
Normal file
753
src/governance-object.cpp
Normal file
@ -0,0 +1,753 @@
|
||||
// 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"
|
||||
|
||||
#include "flat-database.h"
|
||||
#include "governance.h"
|
||||
#include "governance-object.h"
|
||||
#include "governance-vote.h"
|
||||
#include "governance-classes.h"
|
||||
#include "masternode.h"
|
||||
#include "governance.h"
|
||||
#include "darksend.h"
|
||||
#include "masternodeman.h"
|
||||
#include "masternode-sync.h"
|
||||
#include "netfulfilledman.h"
|
||||
#include "util.h"
|
||||
#include "addrman.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <univalue.h>
|
||||
|
||||
CGovernanceObject::CGovernanceObject()
|
||||
: cs(),
|
||||
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
||||
nHashParent(),
|
||||
nRevision(0),
|
||||
nTime(0),
|
||||
nDeletionTime(0),
|
||||
nCollateralHash(),
|
||||
strData(),
|
||||
vinMasternode(),
|
||||
vchSig(),
|
||||
fCachedLocalValidity(false),
|
||||
strLocalValidityError(),
|
||||
fCachedFunding(false),
|
||||
fCachedValid(true),
|
||||
fCachedDelete(false),
|
||||
fCachedEndorsed(false),
|
||||
fDirtyCache(true),
|
||||
fExpired(false),
|
||||
fUnparsable(false),
|
||||
mapCurrentMNVotes(),
|
||||
mapOrphanVotes(),
|
||||
fileVotes()
|
||||
{
|
||||
// PARSE JSON DATA STORAGE (STRDATA)
|
||||
LoadData();
|
||||
}
|
||||
|
||||
CGovernanceObject::CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTimeIn, uint256 nCollateralHashIn, std::string strDataIn)
|
||||
: cs(),
|
||||
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
||||
nHashParent(nHashParentIn),
|
||||
nRevision(nRevisionIn),
|
||||
nTime(nTimeIn),
|
||||
nDeletionTime(0),
|
||||
nCollateralHash(nCollateralHashIn),
|
||||
strData(strDataIn),
|
||||
vinMasternode(),
|
||||
vchSig(),
|
||||
fCachedLocalValidity(false),
|
||||
strLocalValidityError(),
|
||||
fCachedFunding(false),
|
||||
fCachedValid(true),
|
||||
fCachedDelete(false),
|
||||
fCachedEndorsed(false),
|
||||
fDirtyCache(true),
|
||||
fExpired(false),
|
||||
fUnparsable(false),
|
||||
mapCurrentMNVotes(),
|
||||
mapOrphanVotes(),
|
||||
fileVotes()
|
||||
{
|
||||
// PARSE JSON DATA STORAGE (STRDATA)
|
||||
LoadData();
|
||||
}
|
||||
|
||||
CGovernanceObject::CGovernanceObject(const CGovernanceObject& other)
|
||||
: cs(),
|
||||
nObjectType(other.nObjectType),
|
||||
nHashParent(other.nHashParent),
|
||||
nRevision(other.nRevision),
|
||||
nTime(other.nTime),
|
||||
nDeletionTime(other.nDeletionTime),
|
||||
nCollateralHash(other.nCollateralHash),
|
||||
strData(other.strData),
|
||||
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),
|
||||
fExpired(other.fExpired),
|
||||
fUnparsable(other.fUnparsable),
|
||||
mapCurrentMNVotes(other.mapCurrentMNVotes),
|
||||
mapOrphanVotes(other.mapOrphanVotes),
|
||||
fileVotes(other.fileVotes)
|
||||
{}
|
||||
|
||||
bool CGovernanceObject::ProcessVote(CNode* pfrom,
|
||||
const CGovernanceVote& vote,
|
||||
CGovernanceException& exception)
|
||||
{
|
||||
int nMNIndex = governance.GetMasternodeIndex(vote.GetVinMasternode());
|
||||
if(nMNIndex < 0) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Masternode index not found\n";
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING);
|
||||
if(mapOrphanVotes.Insert(vote.GetVinMasternode(), vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) {
|
||||
if(pfrom) {
|
||||
mnodeman.AskForMN(pfrom, vote.GetVinMasternode());
|
||||
}
|
||||
LogPrintf(ostr.str().c_str());
|
||||
}
|
||||
else {
|
||||
LogPrint("gobject", ostr.str().c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vote_m_it it = mapCurrentMNVotes.find(nMNIndex);
|
||||
if(it == mapCurrentMNVotes.end()) {
|
||||
it = mapCurrentMNVotes.insert(vote_m_t::value_type(nMNIndex,vote_rec_t())).first;
|
||||
}
|
||||
vote_rec_t& recVote = it->second;
|
||||
vote_signal_enum_t eSignal = vote.GetSignal();
|
||||
if(eSignal == VOTE_SIGNAL_NONE) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Vote signal: none" << "\n";
|
||||
LogPrint("gobject", ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING);
|
||||
return false;
|
||||
}
|
||||
if(eSignal > MAX_SUPPORTED_VOTE_SIGNAL) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Unsupported vote signal:" << CGovernanceVoting::ConvertSignalToString(vote.GetSignal()) << "\n";
|
||||
LogPrintf(ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
|
||||
return false;
|
||||
}
|
||||
vote_instance_m_it it2 = recVote.mapInstances.find(int(eSignal));
|
||||
if(it2 == recVote.mapInstances.end()) {
|
||||
it2 = recVote.mapInstances.insert(vote_instance_m_t::value_type(int(eSignal), vote_instance_t())).first;
|
||||
}
|
||||
vote_instance_t& voteInstance = it2->second;
|
||||
int64_t nNow = GetTime();
|
||||
if(governance.AreRateChecksEnabled()) {
|
||||
int64_t nTimeDelta = nNow - voteInstance.nTime;
|
||||
if(nTimeDelta < GOVERNANCE_UPDATE_MIN) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Masternode voting too often "
|
||||
<< ", MN outpoint = " << vote.GetVinMasternode().prevout.ToStringShort()
|
||||
<< ", governance object hash = " << GetHash().ToString()
|
||||
<< ", time delta = " << nTimeDelta << "\n";
|
||||
LogPrint("gobject", ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_TEMPORARY_ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Finally check that the vote is actually valid (done last because of cost of signature verification)
|
||||
if(!vote.IsValid(true)) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Invalid vote "
|
||||
<< ", MN outpoint = " << vote.GetVinMasternode().prevout.ToStringShort()
|
||||
<< ", governance object hash = " << GetHash().ToString()
|
||||
<< ", vote hash = " << vote.GetHash().ToString() << "\n";
|
||||
LogPrintf(ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
|
||||
governance.AddInvalidVote(vote);
|
||||
return false;
|
||||
}
|
||||
voteInstance = vote_instance_t(vote.GetOutcome(), nNow);
|
||||
fileVotes.AddVote(vote);
|
||||
fDirtyCache = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGovernanceObject::RebuildVoteMap()
|
||||
{
|
||||
vote_m_t mapMNVotesNew;
|
||||
for(vote_m_it it = mapCurrentMNVotes.begin(); it != mapCurrentMNVotes.end(); ++it) {
|
||||
CTxIn vinMasternode;
|
||||
if(mnodeman.GetMasternodeVinForIndexOld(it->first, vinMasternode)) {
|
||||
int nNewIndex = mnodeman.GetMasternodeIndex(vinMasternode);
|
||||
if((nNewIndex >= 0)) {
|
||||
mapMNVotesNew[nNewIndex] = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
mapCurrentMNVotes = mapMNVotesNew;
|
||||
}
|
||||
|
||||
void CGovernanceObject::ClearMasternodeVotes()
|
||||
{
|
||||
vote_m_it it = mapCurrentMNVotes.begin();
|
||||
while(it != mapCurrentMNVotes.end()) {
|
||||
bool fIndexRebuilt = false;
|
||||
CTxIn vinMasternode;
|
||||
bool fRemove = true;
|
||||
if(mnodeman.Get(it->first, vinMasternode, fIndexRebuilt)) {
|
||||
if(mnodeman.Has(vinMasternode)) {
|
||||
fRemove = false;
|
||||
}
|
||||
else {
|
||||
fileVotes.RemoveVotesFromMasternode(vinMasternode);
|
||||
}
|
||||
}
|
||||
|
||||
if(fRemove) {
|
||||
mapCurrentMNVotes.erase(it++);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGovernanceObject::SetMasternodeInfo(const CTxIn& vin)
|
||||
{
|
||||
vinMasternode = vin;
|
||||
}
|
||||
|
||||
bool CGovernanceObject::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
||||
LogPrintf("CGovernanceObject::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
LogPrint("gobject", "CGovernanceObject::Sign -- pubkey id = %s, vin = %s\n",
|
||||
pubKeyMasternode.GetID().ToString(), vinMasternode.prevout.ToStringShort());
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGovernanceObject::CheckSignature(CPubKey& pubKeyMasternode)
|
||||
{
|
||||
LOCK(cs);
|
||||
std::string strError;
|
||||
uint256 nHash = GetHash();
|
||||
std::string strMessage = nHash.ToString();
|
||||
|
||||
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
||||
LogPrintf("CGovernance::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
DBG( printf("CGovernanceObject::GetHash %i %li %s\n", nRevision, nTime, strData.c_str()); );
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the actual object from the strData JSON structure.
|
||||
|
||||
Returns an empty object on error.
|
||||
*/
|
||||
UniValue CGovernanceObject::GetJSONObject()
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
if(strData.empty()) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue objResult(UniValue::VOBJ);
|
||||
GetData(objResult);
|
||||
|
||||
std::vector<UniValue> arr1 = objResult.getValues();
|
||||
std::vector<UniValue> arr2 = arr1.at( 0 ).getValues();
|
||||
obj = arr2.at( 1 );
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* LoadData
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
* Attempt to load data from strData
|
||||
*
|
||||
*/
|
||||
|
||||
void CGovernanceObject::LoadData()
|
||||
{
|
||||
// todo : 12.1 - resolved
|
||||
//return;
|
||||
|
||||
if(strData.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// ATTEMPT TO LOAD JSON STRING FROM STRDATA
|
||||
UniValue objResult(UniValue::VOBJ);
|
||||
GetData(objResult);
|
||||
|
||||
DBG( cout << "CGovernanceObject::LoadData strData = "
|
||||
<< GetDataAsString()
|
||||
<< endl; );
|
||||
|
||||
UniValue obj = GetJSONObject();
|
||||
nObjectType = obj["type"].get_int();
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
fUnparsable = true;
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::LoadData Error parsing JSON"
|
||||
<< ", e.what() = " << e.what();
|
||||
DBG( cout << ostr.str() << endl; );
|
||||
LogPrintf( ostr.str().c_str() );
|
||||
return;
|
||||
}
|
||||
catch(...) {
|
||||
fUnparsable = true;
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::LoadData Unknown Error parsing JSON";
|
||||
DBG( cout << ostr.str() << endl; );
|
||||
LogPrintf( ostr.str().c_str() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GetData - Example usage:
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
* Decode governance object data into UniValue(VOBJ)
|
||||
*
|
||||
*/
|
||||
|
||||
void CGovernanceObject::GetData(UniValue& objResult)
|
||||
{
|
||||
UniValue o(UniValue::VOBJ);
|
||||
std::string s = GetDataAsString();
|
||||
o.read(s);
|
||||
objResult = o;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetData - As
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
std::string CGovernanceObject::GetDataAsHex()
|
||||
{
|
||||
return strData;
|
||||
}
|
||||
|
||||
std::string CGovernanceObject::GetDataAsString()
|
||||
{
|
||||
std::vector<unsigned char> v = ParseHex(strData);
|
||||
std::string s(v.begin(), v.end());
|
||||
|
||||
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)
|
||||
{
|
||||
bool fMissingMasternode = false;
|
||||
|
||||
return IsValidLocally(pindex, strError, fMissingMasternode, fCheckCollateral);
|
||||
}
|
||||
|
||||
bool CGovernanceObject::IsValidLocally(const CBlockIndex* pindex, std::string& strError, bool& fMissingMasternode, bool fCheckCollateral)
|
||||
{
|
||||
fMissingMasternode = false;
|
||||
if(!pindex) {
|
||||
strError = "Tip is NULL";
|
||||
return true;
|
||||
}
|
||||
|
||||
if(fUnparsable) {
|
||||
strError = "Object data unparseable";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(nObjectType) {
|
||||
case GOVERNANCE_OBJECT_PROPOSAL:
|
||||
case GOVERNANCE_OBJECT_TRIGGER:
|
||||
case GOVERNANCE_OBJECT_WATCHDOG:
|
||||
break;
|
||||
default:
|
||||
strError = strprintf("Invalid object type %d", nObjectType);
|
||||
return false;
|
||||
}
|
||||
|
||||
// IF ABSOLUTE NO COUNT (NO-YES VALID VOTES) IS MORE THAN 10% OF THE NETWORK MASTERNODES, OBJ IS INVALID
|
||||
|
||||
if(GetAbsoluteNoCount(VOTE_SIGNAL_VALID) > mnodeman.CountEnabled(MIN_GOVERNANCE_PEER_PROTO_VERSION)/10) {
|
||||
strError = "Automated removal";
|
||||
return false;
|
||||
}
|
||||
|
||||
// CHECK COLLATERAL IF REQUIRED (HIGH CPU USAGE)
|
||||
|
||||
if(fCheckCollateral) {
|
||||
if((nObjectType == GOVERNANCE_OBJECT_TRIGGER) || (nObjectType == GOVERNANCE_OBJECT_WATCHDOG)) {
|
||||
std::string strOutpoint = vinMasternode.prevout.ToStringShort();
|
||||
masternode_info_t infoMn = mnodeman.GetMasternodeInfo(vinMasternode);
|
||||
if(!infoMn.fInfoValid) {
|
||||
fMissingMasternode = true;
|
||||
strError = "Masternode not found: " + strOutpoint;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that we have a valid MN signature
|
||||
if(!CheckSignature(infoMn.pubKeyMasternode)) {
|
||||
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey id = " + infoMn.pubKeyMasternode.GetID().ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
if(!governance.MasternodeRateCheck(vinMasternode, nObjectType)) {
|
||||
strError = "Masternode attempting to create too many objects: " + strOutpoint;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!IsCollateralValid(strError)) {
|
||||
// strError set in IsCollateralValid
|
||||
if(strError == "") strError = "Collateral is invalid";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
*/
|
||||
|
||||
// 12.1 - todo - compile error
|
||||
// if(address.IsPayToScriptHash()) {
|
||||
// strError = "Governance system - multisig is not currently supported";
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CAmount CGovernanceObject::GetMinCollateralFee()
|
||||
{
|
||||
// Only 1 type has a fee for the moment but switch statement allows for future object types
|
||||
switch(nObjectType) {
|
||||
case GOVERNANCE_OBJECT_PROPOSAL: return GOVERNANCE_PROPOSAL_FEE_TX;
|
||||
case GOVERNANCE_OBJECT_TRIGGER: return 0;
|
||||
case GOVERNANCE_OBJECT_WATCHDOG: return 0;
|
||||
default: return MAX_MONEY;
|
||||
}
|
||||
}
|
||||
|
||||
bool CGovernanceObject::IsCollateralValid(std::string& strError)
|
||||
{
|
||||
strError = "";
|
||||
CAmount nMinFee = GetMinCollateralFee();
|
||||
uint256 nExpectedHash = GetHash();
|
||||
|
||||
CTransaction txCollateral;
|
||||
uint256 nBlockHash;
|
||||
|
||||
// RETRIEVE TRANSACTION IN QUESTION
|
||||
|
||||
if(!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash, true)){
|
||||
strError = strprintf("Can't find collateral tx %s", txCollateral.ToString());
|
||||
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(txCollateral.vout.size() < 1) {
|
||||
strError = strprintf("tx vout size less than 1 | %d", txCollateral.vout.size());
|
||||
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
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) {
|
||||
DBG( cout << "IsCollateralValid txout : " << o.ToString()
|
||||
<< ", 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());
|
||||
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
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());
|
||||
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
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);
|
||||
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s - %d confirmations\n", strError, nConfirmationsIn);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const
|
||||
{
|
||||
int nCount = 0;
|
||||
for(vote_m_cit it = mapCurrentMNVotes.begin(); it != mapCurrentMNVotes.end(); ++it) {
|
||||
const vote_rec_t& recVote = it->second;
|
||||
vote_instance_m_cit it2 = recVote.mapInstances.find(eVoteSignalIn);
|
||||
if(it2 == recVote.mapInstances.end()) {
|
||||
continue;
|
||||
}
|
||||
const vote_instance_t& voteInstance = it2->second;
|
||||
if(voteInstance.eOutcome == eVoteOutcomeIn) {
|
||||
++nCount;
|
||||
}
|
||||
}
|
||||
return nCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific vote counts for each outcome (funding, validity, etc)
|
||||
*/
|
||||
|
||||
int CGovernanceObject::GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return GetYesCount(eVoteSignalIn) - GetNoCount(eVoteSignalIn);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return GetNoCount(eVoteSignalIn) - GetYesCount(eVoteSignalIn);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetYesCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return CountMatchingVotes(eVoteSignalIn, VOTE_OUTCOME_YES);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetNoCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return CountMatchingVotes(eVoteSignalIn, VOTE_OUTCOME_NO);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetAbstainCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return CountMatchingVotes(eVoteSignalIn, VOTE_OUTCOME_ABSTAIN);
|
||||
}
|
||||
|
||||
bool CGovernanceObject::GetCurrentMNVotes(const CTxIn& mnCollateralOutpoint, vote_rec_t& voteRecord)
|
||||
{
|
||||
int nMNIndex = governance.GetMasternodeIndex(mnCollateralOutpoint);
|
||||
vote_m_it it = mapCurrentMNVotes.find(nMNIndex);
|
||||
if (it == mapCurrentMNVotes.end()) {
|
||||
return false;
|
||||
}
|
||||
voteRecord = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGovernanceObject::Relay()
|
||||
{
|
||||
CInv inv(MSG_GOVERNANCE_OBJECT, GetHash());
|
||||
RelayInv(inv, PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
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
|
||||
int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nMnCount / 10);
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
nDeletionTime = GetAdjustedTime();
|
||||
}
|
||||
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.nDeletionTime, second.nDeletionTime);
|
||||
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);
|
||||
}
|
||||
|
||||
void CGovernanceObject::CheckOrphanVotes()
|
||||
{
|
||||
int64_t nNow = GetAdjustedTime();
|
||||
const vote_mcache_t::list_t& listVotes = mapOrphanVotes.GetItemList();
|
||||
vote_mcache_t::list_cit it = listVotes.begin();
|
||||
while(it != listVotes.end()) {
|
||||
bool fRemove = false;
|
||||
const CTxIn& key = it->key;
|
||||
const vote_time_pair_t& pairVote = it->value;
|
||||
const CGovernanceVote& vote = pairVote.first;
|
||||
if(pairVote.second < nNow) {
|
||||
fRemove = true;
|
||||
}
|
||||
else if(!mnodeman.Has(vote.GetVinMasternode())) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
CGovernanceException exception;
|
||||
if(!ProcessVote(NULL, vote, exception)) {
|
||||
LogPrintf("CGovernanceObject::CheckOrphanVotes -- Failed to add orphan vote: %s\n", exception.what());
|
||||
}
|
||||
else {
|
||||
fRemove = true;
|
||||
}
|
||||
++it;
|
||||
if(fRemove) {
|
||||
mapOrphanVotes.Erase(key, pairVote);
|
||||
}
|
||||
}
|
||||
}
|
350
src/governance-object.h
Normal file
350
src/governance-object.h
Normal file
@ -0,0 +1,350 @@
|
||||
// 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.
|
||||
|
||||
#ifndef GOVERNANCE_OBJECT_H
|
||||
#define GOVERNANCE_OBJECT_H
|
||||
|
||||
//#define ENABLE_DASH_DEBUG
|
||||
|
||||
#include "util.h"
|
||||
#include "main.h"
|
||||
#include "sync.h"
|
||||
#include "net.h"
|
||||
#include "key.h"
|
||||
#include "util.h"
|
||||
#include "base58.h"
|
||||
#include "masternode.h"
|
||||
#include "governance-exceptions.h"
|
||||
#include "governance-vote.h"
|
||||
#include "governance-votedb.h"
|
||||
#include "masternodeman.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "init.h"
|
||||
#include <univalue.h>
|
||||
#include "utilstrencodings.h"
|
||||
#include "cachemap.h"
|
||||
#include "cachemultimap.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
class CGovernanceManager;
|
||||
class CGovernanceTriggerManager;
|
||||
class CGovernanceObject;
|
||||
class CGovernanceVote;
|
||||
|
||||
static const int MAX_GOVERNANCE_OBJECT_DATA_SIZE = 16 * 1024;
|
||||
static const int MIN_GOVERNANCE_PEER_PROTO_VERSION = 70203;
|
||||
|
||||
static const int GOVERNANCE_OBJECT_UNKNOWN = 0;
|
||||
static const int GOVERNANCE_OBJECT_PROPOSAL = 1;
|
||||
static const int GOVERNANCE_OBJECT_TRIGGER = 2;
|
||||
static const int GOVERNANCE_OBJECT_WATCHDOG = 3;
|
||||
|
||||
static const CAmount GOVERNANCE_PROPOSAL_FEE_TX = (0.33*COIN);
|
||||
|
||||
static const int64_t GOVERNANCE_FEE_CONFIRMATIONS = 6;
|
||||
static const int64_t GOVERNANCE_UPDATE_MIN = 60*60;
|
||||
static const int64_t GOVERNANCE_DELETION_DELAY = 10*60;
|
||||
static const int64_t GOVERNANCE_ORPHAN_EXPIRATION_TIME = 10*60;
|
||||
|
||||
|
||||
// FOR SEEN MAP ARRAYS - GOVERNANCE OBJECTS AND VOTES
|
||||
static const int SEEN_OBJECT_IS_VALID = 0;
|
||||
static const int SEEN_OBJECT_ERROR_INVALID = 1;
|
||||
static const int SEEN_OBJECT_ERROR_IMMATURE = 2;
|
||||
static const int SEEN_OBJECT_EXECUTED = 3; //used for triggers
|
||||
static const int SEEN_OBJECT_UNKNOWN = 4; // the default
|
||||
|
||||
typedef std::pair<CGovernanceVote, int64_t> vote_time_pair_t;
|
||||
|
||||
inline bool operator<(const vote_time_pair_t& p1, const vote_time_pair_t& p2)
|
||||
{
|
||||
return (p1.first < p2.first);
|
||||
}
|
||||
|
||||
struct vote_instance_t {
|
||||
|
||||
vote_outcome_enum_t eOutcome;
|
||||
int64_t nTime;
|
||||
|
||||
vote_instance_t(vote_outcome_enum_t eOutcomeIn = VOTE_OUTCOME_NONE, int64_t nTimeIn = 0)
|
||||
: eOutcome(eOutcomeIn),
|
||||
nTime(nTimeIn)
|
||||
{}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||
{
|
||||
int nOutcome = int(eOutcome);
|
||||
READWRITE(nOutcome);
|
||||
READWRITE(nTime);
|
||||
if(ser_action.ForRead()) {
|
||||
eOutcome = vote_outcome_enum_t(nOutcome);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<int,vote_instance_t> vote_instance_m_t;
|
||||
|
||||
typedef vote_instance_m_t::iterator vote_instance_m_it;
|
||||
|
||||
typedef vote_instance_m_t::const_iterator vote_instance_m_cit;
|
||||
|
||||
struct vote_rec_t {
|
||||
vote_instance_m_t mapInstances;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||
{
|
||||
READWRITE(mapInstances);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Governance Object
|
||||
*
|
||||
*/
|
||||
|
||||
class CGovernanceObject
|
||||
{
|
||||
friend class CGovernanceManager;
|
||||
|
||||
friend class CGovernanceTriggerManager;
|
||||
|
||||
public: // Types
|
||||
typedef std::map<int, vote_rec_t> vote_m_t;
|
||||
|
||||
typedef vote_m_t::iterator vote_m_it;
|
||||
|
||||
typedef vote_m_t::const_iterator vote_m_cit;
|
||||
|
||||
typedef CacheMultiMap<CTxIn, vote_time_pair_t> vote_mcache_t;
|
||||
|
||||
private:
|
||||
/// critical section to protect the inner data structures
|
||||
mutable CCriticalSection cs;
|
||||
|
||||
/// Object typecode
|
||||
int nObjectType;
|
||||
|
||||
/// parent object, 0 is root
|
||||
uint256 nHashParent;
|
||||
|
||||
/// object revision in the system
|
||||
int nRevision;
|
||||
|
||||
/// time this object was created
|
||||
int64_t nTime;
|
||||
|
||||
/// time this object was marked for deletion
|
||||
int64_t nDeletionTime;
|
||||
|
||||
/// fee-tx
|
||||
uint256 nCollateralHash;
|
||||
|
||||
/// Data field - can be used for anything
|
||||
std::string strData;
|
||||
|
||||
/// Masternode info for signed objects
|
||||
CTxIn vinMasternode;
|
||||
std::vector<unsigned char> vchSig;
|
||||
|
||||
/// is valid by blockchain
|
||||
bool fCachedLocalValidity;
|
||||
std::string strLocalValidityError;
|
||||
|
||||
// VARIOUS FLAGS FOR OBJECT / SET VIA MASTERNODE VOTING
|
||||
|
||||
/// true == minimum network support has been reached for this object to be funded (doesn't mean it will for sure though)
|
||||
bool fCachedFunding;
|
||||
|
||||
/// true == minimum network has been reached flagging this object as a valid and understood goverance object (e.g, the serialized data is correct format, etc)
|
||||
bool fCachedValid;
|
||||
|
||||
/// true == minimum network support has been reached saying this object should be deleted from the system entirely
|
||||
bool fCachedDelete;
|
||||
|
||||
/** true == minimum network support has been reached flagging this object as endorsed by an elected representative body
|
||||
* (e.g. business review board / technecial review board /etc)
|
||||
*/
|
||||
bool fCachedEndorsed;
|
||||
|
||||
/// object was updated and cached values should be updated soon
|
||||
bool fDirtyCache;
|
||||
|
||||
/// Object is no longer of interest
|
||||
bool fExpired;
|
||||
|
||||
/// Failed to parse object data
|
||||
bool fUnparsable;
|
||||
|
||||
vote_m_t mapCurrentMNVotes;
|
||||
|
||||
/// Limited map of votes orphaned by MN
|
||||
vote_mcache_t mapOrphanVotes;
|
||||
|
||||
CGovernanceObjectVoteFile fileVotes;
|
||||
|
||||
public:
|
||||
CGovernanceObject();
|
||||
|
||||
CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTime, uint256 nCollateralHashIn, std::string strDataIn);
|
||||
|
||||
CGovernanceObject(const CGovernanceObject& other);
|
||||
|
||||
void swap(CGovernanceObject& first, CGovernanceObject& second); // nothrow
|
||||
|
||||
// Public Getter methods
|
||||
|
||||
int64_t GetCreationTime() const {
|
||||
return nTime;
|
||||
}
|
||||
|
||||
int64_t GetDeletionTime() const {
|
||||
return nDeletionTime;
|
||||
}
|
||||
|
||||
int GetObjectType() const {
|
||||
return nObjectType;
|
||||
}
|
||||
|
||||
const uint256& GetCollateralHash() const {
|
||||
return nCollateralHash;
|
||||
}
|
||||
|
||||
const CTxIn& GetMasternodeVin() const {
|
||||
return vinMasternode;
|
||||
}
|
||||
|
||||
bool IsSetCachedFunding() const {
|
||||
return fCachedFunding;
|
||||
}
|
||||
|
||||
bool IsSetCachedValid() const {
|
||||
return fCachedValid;
|
||||
}
|
||||
|
||||
bool IsSetCachedDelete() const {
|
||||
return fCachedDelete;
|
||||
}
|
||||
|
||||
bool IsSetCachedEndorsed() const {
|
||||
return fCachedEndorsed;
|
||||
}
|
||||
|
||||
bool IsSetDirtyCache() const {
|
||||
return fDirtyCache;
|
||||
}
|
||||
|
||||
bool IsSetExpired() const {
|
||||
return fExpired;
|
||||
}
|
||||
|
||||
void InvalidateVoteCache() {
|
||||
fDirtyCache = true;
|
||||
}
|
||||
|
||||
CGovernanceObjectVoteFile& GetVoteFile() {
|
||||
return fileVotes;
|
||||
}
|
||||
|
||||
// Signature related functions
|
||||
|
||||
void SetMasternodeInfo(const CTxIn& vin);
|
||||
bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode);
|
||||
bool CheckSignature(CPubKey& pubKeyMasternode);
|
||||
|
||||
// CORE OBJECT FUNCTIONS
|
||||
|
||||
bool IsValidLocally(const CBlockIndex* pindex, std::string& strError, bool fCheckCollateral);
|
||||
|
||||
bool IsValidLocally(const CBlockIndex* pindex, std::string& strError, bool& fMissingMasternode, bool fCheckCollateral);
|
||||
|
||||
/// Check the collateral transaction for the budget proposal/finalized budget
|
||||
bool IsCollateralValid(std::string& strError);
|
||||
|
||||
void UpdateLocalValidity(const CBlockIndex *pCurrentBlockIndex);
|
||||
|
||||
void UpdateSentinelVariables(const CBlockIndex *pCurrentBlockIndex);
|
||||
|
||||
int GetObjectSubtype();
|
||||
|
||||
CAmount GetMinCollateralFee();
|
||||
|
||||
UniValue GetJSONObject();
|
||||
|
||||
void Relay();
|
||||
|
||||
uint256 GetHash();
|
||||
|
||||
// GET VOTE COUNT FOR SIGNAL
|
||||
|
||||
int CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const;
|
||||
|
||||
int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetYesCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetNoCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetAbstainCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
|
||||
bool GetCurrentMNVotes(const CTxIn& mnCollateralOutpoint, vote_rec_t& voteRecord);
|
||||
|
||||
// FUNCTIONS FOR DEALING WITH DATA STRING
|
||||
|
||||
std::string GetDataAsHex();
|
||||
std::string GetDataAsString();
|
||||
|
||||
// SERIALIZER
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||
{
|
||||
// SERIALIZE DATA FOR SAVING/LOADING OR NETWORK FUNCTIONS
|
||||
|
||||
READWRITE(nHashParent);
|
||||
READWRITE(nRevision);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nCollateralHash);
|
||||
READWRITE(LIMITED_STRING(strData, MAX_GOVERNANCE_OBJECT_DATA_SIZE));
|
||||
READWRITE(nObjectType);
|
||||
READWRITE(vinMasternode);
|
||||
READWRITE(vchSig);
|
||||
if(nType & SER_DISK) {
|
||||
// Only include these for the disk file format
|
||||
LogPrint("gobject", "CGovernanceObject::SerializationOp Reading/writing votes from/to disk\n");
|
||||
READWRITE(mapCurrentMNVotes);
|
||||
READWRITE(fileVotes);
|
||||
LogPrint("gobject", "CGovernanceObject::SerializationOp hash = %s, vote count = %d\n", GetHash().ToString(), fileVotes.GetVoteCount());
|
||||
}
|
||||
|
||||
// AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY
|
||||
}
|
||||
|
||||
private:
|
||||
// FUNCTIONS FOR DEALING WITH DATA STRING
|
||||
void LoadData();
|
||||
void GetData(UniValue& objResult);
|
||||
|
||||
bool ProcessVote(CNode* pfrom,
|
||||
const CGovernanceVote& vote,
|
||||
CGovernanceException& exception);
|
||||
|
||||
void RebuildVoteMap();
|
||||
|
||||
/// Called when MN's which have voted on this object have been removed
|
||||
void ClearMasternodeVotes();
|
||||
|
||||
void CheckOrphanVotes();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "flat-database.h"
|
||||
#include "governance.h"
|
||||
#include "governance-object.h"
|
||||
#include "governance-vote.h"
|
||||
#include "governance-classes.h"
|
||||
#include "masternode.h"
|
||||
@ -176,7 +177,7 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, std::string& strCommand, C
|
||||
bool fIsValid = govobj.IsValidLocally(pCurrentBlockIndex, strError, fMasternodeMissing, true);
|
||||
|
||||
if(fMasternodeMissing) {
|
||||
mapMasternodeOrphanObjects.insert(std::make_pair(govobj.GetHash(), govobj));
|
||||
mapMasternodeOrphanObjects.insert(std::make_pair(govobj.GetHash(), object_time_pair_t(govobj, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME)));
|
||||
LogPrint("gobject", "CGovernanceManager -- Missing masternode for: %s\n", strHash);
|
||||
// fIsValid must also be false here so we will return early in the next if block
|
||||
}
|
||||
@ -248,21 +249,30 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, std::string& strCommand, C
|
||||
void CGovernanceManager::CheckOrphanVotes(CNode* pfrom, CGovernanceObject& govobj, CGovernanceException& exception)
|
||||
{
|
||||
uint256 nHash = govobj.GetHash();
|
||||
std::vector<CGovernanceVote> vecVotes;
|
||||
mapOrphanVotes.GetAll(nHash, vecVotes);
|
||||
std::vector<vote_time_pair_t> vecVotePairs;
|
||||
mapOrphanVotes.GetAll(nHash, vecVotePairs);
|
||||
|
||||
for(size_t i = 0; i < vecVotes.size(); ++i) {
|
||||
CGovernanceVote& vote = vecVotes[i];
|
||||
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;
|
||||
CGovernanceException exception;
|
||||
if(govobj.ProcessVote(pfrom, vote, exception)) {
|
||||
vecVotes[i].Relay();
|
||||
mapOrphanVotes.Erase(nHash, vote);
|
||||
if(pairVote.second < nNow) {
|
||||
fRemove = true;
|
||||
}
|
||||
else if(govobj.ProcessVote(pfrom, vote, exception)) {
|
||||
vote.Relay();
|
||||
fRemove = true;
|
||||
}
|
||||
else {
|
||||
if((exception.GetNodePenalty() != 0) && masternodeSync.IsSynced()) {
|
||||
Misbehaving(pfrom->GetId(), exception.GetNodePenalty());
|
||||
}
|
||||
}
|
||||
if(fRemove) {
|
||||
mapOrphanVotes.Erase(nHash, pairVote);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,7 +692,7 @@ bool CGovernanceManager::ProcessVote(CNode* pfrom, const CGovernanceVote& vote,
|
||||
<< ", MN outpoint = " << vote.GetVinMasternode().prevout.ToStringShort()
|
||||
<< ", governance object hash = " << vote.GetParentHash().ToString() << "\n";
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING);
|
||||
if(mapOrphanVotes.Insert(nHashGovobj, vote)) {
|
||||
if(mapOrphanVotes.Insert(nHashGovobj, vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) {
|
||||
RequestGovernanceObject(pfrom, nHashGovobj);
|
||||
LogPrintf(ostr.str().c_str());
|
||||
}
|
||||
@ -719,10 +729,17 @@ void CGovernanceManager::CheckMasternodeOrphanVotes()
|
||||
void CGovernanceManager::CheckMasternodeOrphanObjects()
|
||||
{
|
||||
LOCK(cs);
|
||||
int64_t nNow = GetAdjustedTime();
|
||||
fRateChecksEnabled = false;
|
||||
object_m_it it = mapMasternodeOrphanObjects.begin();
|
||||
object_time_m_it it = mapMasternodeOrphanObjects.begin();
|
||||
while(it != mapMasternodeOrphanObjects.end()) {
|
||||
CGovernanceObject& govobj = it->second;
|
||||
object_time_pair_t& pair = it->second;
|
||||
CGovernanceObject& govobj = pair.first;
|
||||
|
||||
if(pair.second < nNow) {
|
||||
mapMasternodeOrphanObjects.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
string strError;
|
||||
bool fMasternodeMissing = false;
|
||||
@ -828,639 +845,6 @@ void CGovernanceManager::AddCachedTriggers()
|
||||
}
|
||||
}
|
||||
|
||||
CGovernanceObject::CGovernanceObject()
|
||||
: cs(),
|
||||
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
||||
nHashParent(),
|
||||
nRevision(0),
|
||||
nTime(0),
|
||||
nDeletionTime(0),
|
||||
nCollateralHash(),
|
||||
strData(),
|
||||
vinMasternode(),
|
||||
vchSig(),
|
||||
fCachedLocalValidity(false),
|
||||
strLocalValidityError(),
|
||||
fCachedFunding(false),
|
||||
fCachedValid(true),
|
||||
fCachedDelete(false),
|
||||
fCachedEndorsed(false),
|
||||
fDirtyCache(true),
|
||||
fExpired(false),
|
||||
fUnparsable(false),
|
||||
mapCurrentMNVotes(),
|
||||
mapOrphanVotes(),
|
||||
fileVotes()
|
||||
{
|
||||
// PARSE JSON DATA STORAGE (STRDATA)
|
||||
LoadData();
|
||||
}
|
||||
|
||||
CGovernanceObject::CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTimeIn, uint256 nCollateralHashIn, std::string strDataIn)
|
||||
: cs(),
|
||||
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
||||
nHashParent(nHashParentIn),
|
||||
nRevision(nRevisionIn),
|
||||
nTime(nTimeIn),
|
||||
nDeletionTime(0),
|
||||
nCollateralHash(nCollateralHashIn),
|
||||
strData(strDataIn),
|
||||
vinMasternode(),
|
||||
vchSig(),
|
||||
fCachedLocalValidity(false),
|
||||
strLocalValidityError(),
|
||||
fCachedFunding(false),
|
||||
fCachedValid(true),
|
||||
fCachedDelete(false),
|
||||
fCachedEndorsed(false),
|
||||
fDirtyCache(true),
|
||||
fExpired(false),
|
||||
fUnparsable(false),
|
||||
mapCurrentMNVotes(),
|
||||
mapOrphanVotes(),
|
||||
fileVotes()
|
||||
{
|
||||
// PARSE JSON DATA STORAGE (STRDATA)
|
||||
LoadData();
|
||||
}
|
||||
|
||||
CGovernanceObject::CGovernanceObject(const CGovernanceObject& other)
|
||||
: cs(),
|
||||
nObjectType(other.nObjectType),
|
||||
nHashParent(other.nHashParent),
|
||||
nRevision(other.nRevision),
|
||||
nTime(other.nTime),
|
||||
nDeletionTime(other.nDeletionTime),
|
||||
nCollateralHash(other.nCollateralHash),
|
||||
strData(other.strData),
|
||||
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),
|
||||
fExpired(other.fExpired),
|
||||
fUnparsable(other.fUnparsable),
|
||||
mapCurrentMNVotes(other.mapCurrentMNVotes),
|
||||
mapOrphanVotes(other.mapOrphanVotes),
|
||||
fileVotes(other.fileVotes)
|
||||
{}
|
||||
|
||||
bool CGovernanceObject::ProcessVote(CNode* pfrom,
|
||||
const CGovernanceVote& vote,
|
||||
CGovernanceException& exception)
|
||||
{
|
||||
int nMNIndex = governance.GetMasternodeIndex(vote.GetVinMasternode());
|
||||
if(nMNIndex < 0) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Masternode index not found\n";
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING);
|
||||
if(mapOrphanVotes.Insert(vote.GetVinMasternode(), vote)) {
|
||||
if(pfrom) {
|
||||
mnodeman.AskForMN(pfrom, vote.GetVinMasternode());
|
||||
}
|
||||
LogPrintf(ostr.str().c_str());
|
||||
}
|
||||
else {
|
||||
LogPrint("gobject", ostr.str().c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vote_m_it it = mapCurrentMNVotes.find(nMNIndex);
|
||||
if(it == mapCurrentMNVotes.end()) {
|
||||
it = mapCurrentMNVotes.insert(vote_m_t::value_type(nMNIndex,vote_rec_t())).first;
|
||||
}
|
||||
vote_rec_t& recVote = it->second;
|
||||
vote_signal_enum_t eSignal = vote.GetSignal();
|
||||
if(eSignal == VOTE_SIGNAL_NONE) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Vote signal: none" << "\n";
|
||||
LogPrint("gobject", ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING);
|
||||
return false;
|
||||
}
|
||||
if(eSignal > MAX_SUPPORTED_VOTE_SIGNAL) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Unsupported vote signal:" << CGovernanceVoting::ConvertSignalToString(vote.GetSignal()) << "\n";
|
||||
LogPrintf(ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
|
||||
return false;
|
||||
}
|
||||
vote_instance_m_it it2 = recVote.mapInstances.find(int(eSignal));
|
||||
if(it2 == recVote.mapInstances.end()) {
|
||||
it2 = recVote.mapInstances.insert(vote_instance_m_t::value_type(int(eSignal), vote_instance_t())).first;
|
||||
}
|
||||
vote_instance_t& voteInstance = it2->second;
|
||||
int64_t nNow = GetTime();
|
||||
if(governance.AreRateChecksEnabled()) {
|
||||
int64_t nTimeDelta = nNow - voteInstance.nTime;
|
||||
if(nTimeDelta < GOVERNANCE_UPDATE_MIN) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Masternode voting too often "
|
||||
<< ", MN outpoint = " << vote.GetVinMasternode().prevout.ToStringShort()
|
||||
<< ", governance object hash = " << GetHash().ToString()
|
||||
<< ", time delta = " << nTimeDelta << "\n";
|
||||
LogPrint("gobject", ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_TEMPORARY_ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Finally check that the vote is actually valid (done last because of cost of signature verification)
|
||||
if(!vote.IsValid(true)) {
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::ProcessVote -- Invalid vote "
|
||||
<< ", MN outpoint = " << vote.GetVinMasternode().prevout.ToStringShort()
|
||||
<< ", governance object hash = " << GetHash().ToString()
|
||||
<< ", vote hash = " << vote.GetHash().ToString() << "\n";
|
||||
LogPrintf(ostr.str().c_str());
|
||||
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
|
||||
governance.AddInvalidVote(vote);
|
||||
return false;
|
||||
}
|
||||
voteInstance = vote_instance_t(vote.GetOutcome(), nNow);
|
||||
fileVotes.AddVote(vote);
|
||||
fDirtyCache = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGovernanceObject::RebuildVoteMap()
|
||||
{
|
||||
vote_m_t mapMNVotesNew;
|
||||
for(vote_m_it it = mapCurrentMNVotes.begin(); it != mapCurrentMNVotes.end(); ++it) {
|
||||
CTxIn vinMasternode;
|
||||
if(mnodeman.GetMasternodeVinForIndexOld(it->first, vinMasternode)) {
|
||||
int nNewIndex = mnodeman.GetMasternodeIndex(vinMasternode);
|
||||
if((nNewIndex >= 0)) {
|
||||
mapMNVotesNew[nNewIndex] = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
mapCurrentMNVotes = mapMNVotesNew;
|
||||
}
|
||||
|
||||
void CGovernanceObject::ClearMasternodeVotes()
|
||||
{
|
||||
vote_m_it it = mapCurrentMNVotes.begin();
|
||||
while(it != mapCurrentMNVotes.end()) {
|
||||
bool fIndexRebuilt = false;
|
||||
CTxIn vinMasternode;
|
||||
bool fRemove = true;
|
||||
if(mnodeman.Get(it->first, vinMasternode, fIndexRebuilt)) {
|
||||
if(mnodeman.Has(vinMasternode)) {
|
||||
fRemove = false;
|
||||
}
|
||||
else {
|
||||
fileVotes.RemoveVotesFromMasternode(vinMasternode);
|
||||
}
|
||||
}
|
||||
|
||||
if(fRemove) {
|
||||
mapCurrentMNVotes.erase(it++);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGovernanceObject::SetMasternodeInfo(const CTxIn& vin)
|
||||
{
|
||||
vinMasternode = vin;
|
||||
}
|
||||
|
||||
bool CGovernanceObject::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
||||
LogPrintf("CGovernanceObject::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
LogPrint("gobject", "CGovernanceObject::Sign -- pubkey id = %s, vin = %s\n",
|
||||
pubKeyMasternode.GetID().ToString(), vinMasternode.prevout.ToStringShort());
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGovernanceObject::CheckSignature(CPubKey& pubKeyMasternode)
|
||||
{
|
||||
LOCK(cs);
|
||||
std::string strError;
|
||||
uint256 nHash = GetHash();
|
||||
std::string strMessage = nHash.ToString();
|
||||
|
||||
if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
||||
LogPrintf("CGovernance::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
DBG( printf("CGovernanceObject::GetHash %i %li %s\n", nRevision, nTime, strData.c_str()); );
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the actual object from the strData JSON structure.
|
||||
|
||||
Returns an empty object on error.
|
||||
*/
|
||||
UniValue CGovernanceObject::GetJSONObject()
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
if(strData.empty()) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue objResult(UniValue::VOBJ);
|
||||
GetData(objResult);
|
||||
|
||||
std::vector<UniValue> arr1 = objResult.getValues();
|
||||
std::vector<UniValue> arr2 = arr1.at( 0 ).getValues();
|
||||
obj = arr2.at( 1 );
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* LoadData
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
* Attempt to load data from strData
|
||||
*
|
||||
*/
|
||||
|
||||
void CGovernanceObject::LoadData()
|
||||
{
|
||||
// todo : 12.1 - resolved
|
||||
//return;
|
||||
|
||||
if(strData.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// ATTEMPT TO LOAD JSON STRING FROM STRDATA
|
||||
UniValue objResult(UniValue::VOBJ);
|
||||
GetData(objResult);
|
||||
|
||||
DBG( cout << "CGovernanceObject::LoadData strData = "
|
||||
<< GetDataAsString()
|
||||
<< endl; );
|
||||
|
||||
UniValue obj = GetJSONObject();
|
||||
nObjectType = obj["type"].get_int();
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
fUnparsable = true;
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::LoadData Error parsing JSON"
|
||||
<< ", e.what() = " << e.what();
|
||||
DBG( cout << ostr.str() << endl; );
|
||||
LogPrintf( ostr.str().c_str() );
|
||||
return;
|
||||
}
|
||||
catch(...) {
|
||||
fUnparsable = true;
|
||||
std::ostringstream ostr;
|
||||
ostr << "CGovernanceObject::LoadData Unknown Error parsing JSON";
|
||||
DBG( cout << ostr.str() << endl; );
|
||||
LogPrintf( ostr.str().c_str() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GetData - Example usage:
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
* Decode governance object data into UniValue(VOBJ)
|
||||
*
|
||||
*/
|
||||
|
||||
void CGovernanceObject::GetData(UniValue& objResult)
|
||||
{
|
||||
UniValue o(UniValue::VOBJ);
|
||||
std::string s = GetDataAsString();
|
||||
o.read(s);
|
||||
objResult = o;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetData - As
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
std::string CGovernanceObject::GetDataAsHex()
|
||||
{
|
||||
return strData;
|
||||
}
|
||||
|
||||
std::string CGovernanceObject::GetDataAsString()
|
||||
{
|
||||
std::vector<unsigned char> v = ParseHex(strData);
|
||||
std::string s(v.begin(), v.end());
|
||||
|
||||
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)
|
||||
{
|
||||
bool fMissingMasternode = false;
|
||||
|
||||
return IsValidLocally(pindex, strError, fMissingMasternode, fCheckCollateral);
|
||||
}
|
||||
|
||||
bool CGovernanceObject::IsValidLocally(const CBlockIndex* pindex, std::string& strError, bool& fMissingMasternode, bool fCheckCollateral)
|
||||
{
|
||||
fMissingMasternode = false;
|
||||
if(!pindex) {
|
||||
strError = "Tip is NULL";
|
||||
return true;
|
||||
}
|
||||
|
||||
if(fUnparsable) {
|
||||
strError = "Object data unparseable";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(nObjectType) {
|
||||
case GOVERNANCE_OBJECT_PROPOSAL:
|
||||
case GOVERNANCE_OBJECT_TRIGGER:
|
||||
case GOVERNANCE_OBJECT_WATCHDOG:
|
||||
break;
|
||||
default:
|
||||
strError = strprintf("Invalid object type %d", nObjectType);
|
||||
return false;
|
||||
}
|
||||
|
||||
// IF ABSOLUTE NO COUNT (NO-YES VALID VOTES) IS MORE THAN 10% OF THE NETWORK MASTERNODES, OBJ IS INVALID
|
||||
|
||||
if(GetAbsoluteNoCount(VOTE_SIGNAL_VALID) > mnodeman.CountEnabled(MIN_GOVERNANCE_PEER_PROTO_VERSION)/10) {
|
||||
strError = "Automated removal";
|
||||
return false;
|
||||
}
|
||||
|
||||
// CHECK COLLATERAL IF REQUIRED (HIGH CPU USAGE)
|
||||
|
||||
if(fCheckCollateral) {
|
||||
if((nObjectType == GOVERNANCE_OBJECT_TRIGGER) || (nObjectType == GOVERNANCE_OBJECT_WATCHDOG)) {
|
||||
std::string strOutpoint = vinMasternode.prevout.ToStringShort();
|
||||
masternode_info_t infoMn = mnodeman.GetMasternodeInfo(vinMasternode);
|
||||
if(!infoMn.fInfoValid) {
|
||||
fMissingMasternode = true;
|
||||
strError = "Masternode not found: " + strOutpoint;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that we have a valid MN signature
|
||||
if(!CheckSignature(infoMn.pubKeyMasternode)) {
|
||||
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey id = " + infoMn.pubKeyMasternode.GetID().ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
if(!governance.MasternodeRateCheck(vinMasternode, nObjectType)) {
|
||||
strError = "Masternode attempting to create too many objects: " + strOutpoint;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!IsCollateralValid(strError)) {
|
||||
// strError set in IsCollateralValid
|
||||
if(strError == "") strError = "Collateral is invalid";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
*/
|
||||
|
||||
// 12.1 - todo - compile error
|
||||
// if(address.IsPayToScriptHash()) {
|
||||
// strError = "Governance system - multisig is not currently supported";
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CAmount CGovernanceObject::GetMinCollateralFee()
|
||||
{
|
||||
// Only 1 type has a fee for the moment but switch statement allows for future object types
|
||||
switch(nObjectType) {
|
||||
case GOVERNANCE_OBJECT_PROPOSAL: return GOVERNANCE_PROPOSAL_FEE_TX;
|
||||
case GOVERNANCE_OBJECT_TRIGGER: return 0;
|
||||
case GOVERNANCE_OBJECT_WATCHDOG: return 0;
|
||||
default: return MAX_MONEY;
|
||||
}
|
||||
}
|
||||
|
||||
bool CGovernanceObject::IsCollateralValid(std::string& strError)
|
||||
{
|
||||
strError = "";
|
||||
CAmount nMinFee = GetMinCollateralFee();
|
||||
uint256 nExpectedHash = GetHash();
|
||||
|
||||
CTransaction txCollateral;
|
||||
uint256 nBlockHash;
|
||||
|
||||
// RETRIEVE TRANSACTION IN QUESTION
|
||||
|
||||
if(!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash, true)){
|
||||
strError = strprintf("Can't find collateral tx %s", txCollateral.ToString());
|
||||
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(txCollateral.vout.size() < 1) {
|
||||
strError = strprintf("tx vout size less than 1 | %d", txCollateral.vout.size());
|
||||
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
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) {
|
||||
DBG( cout << "IsCollateralValid txout : " << o.ToString()
|
||||
<< ", 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());
|
||||
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
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());
|
||||
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
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);
|
||||
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s - %d confirmations\n", strError, nConfirmationsIn);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const
|
||||
{
|
||||
int nCount = 0;
|
||||
for(vote_m_cit it = mapCurrentMNVotes.begin(); it != mapCurrentMNVotes.end(); ++it) {
|
||||
const vote_rec_t& recVote = it->second;
|
||||
vote_instance_m_cit it2 = recVote.mapInstances.find(eVoteSignalIn);
|
||||
if(it2 == recVote.mapInstances.end()) {
|
||||
continue;
|
||||
}
|
||||
const vote_instance_t& voteInstance = it2->second;
|
||||
if(voteInstance.eOutcome == eVoteOutcomeIn) {
|
||||
++nCount;
|
||||
}
|
||||
}
|
||||
return nCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific vote counts for each outcome (funding, validity, etc)
|
||||
*/
|
||||
|
||||
int CGovernanceObject::GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return GetYesCount(eVoteSignalIn) - GetNoCount(eVoteSignalIn);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return GetNoCount(eVoteSignalIn) - GetYesCount(eVoteSignalIn);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetYesCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return CountMatchingVotes(eVoteSignalIn, VOTE_OUTCOME_YES);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetNoCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return CountMatchingVotes(eVoteSignalIn, VOTE_OUTCOME_NO);
|
||||
}
|
||||
|
||||
int CGovernanceObject::GetAbstainCount(vote_signal_enum_t eVoteSignalIn) const
|
||||
{
|
||||
return CountMatchingVotes(eVoteSignalIn, VOTE_OUTCOME_ABSTAIN);
|
||||
}
|
||||
|
||||
bool CGovernanceObject::GetCurrentMNVotes(const CTxIn& mnCollateralOutpoint, vote_rec_t& voteRecord)
|
||||
{
|
||||
int nMNIndex = governance.GetMasternodeIndex(mnCollateralOutpoint);
|
||||
vote_m_it it = mapCurrentMNVotes.find(nMNIndex);
|
||||
if (it == mapCurrentMNVotes.end()) {
|
||||
return false;
|
||||
}
|
||||
voteRecord = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGovernanceObject::Relay()
|
||||
{
|
||||
CInv inv(MSG_GOVERNANCE_OBJECT, GetHash());
|
||||
RelayInv(inv, PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
std::string CGovernanceManager::ToString() const
|
||||
{
|
||||
std::ostringstream info;
|
||||
@ -1493,84 +877,3 @@ void CGovernanceManager::UpdatedBlockTip(const CBlockIndex *pindex)
|
||||
if(!fLiteMode && masternodeSync.IsSynced())
|
||||
NewBlock();
|
||||
}
|
||||
|
||||
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
|
||||
int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nMnCount / 10);
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
nDeletionTime = GetAdjustedTime();
|
||||
}
|
||||
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.nDeletionTime, second.nDeletionTime);
|
||||
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);
|
||||
}
|
||||
|
||||
void CGovernanceObject::CheckOrphanVotes()
|
||||
{
|
||||
const vote_mcache_t::list_t& listVotes = mapOrphanVotes.GetItemList();
|
||||
for(vote_mcache_t::list_cit it = listVotes.begin(); it != listVotes.end(); ++it) {
|
||||
const CGovernanceVote& vote = it->value;
|
||||
if(!mnodeman.Has(vote.GetVinMasternode())) {
|
||||
continue;
|
||||
}
|
||||
CGovernanceException exception;
|
||||
if(!ProcessVote(NULL, vote, exception)) {
|
||||
LogPrintf("CGovernanceObject::CheckOrphanVotes -- Failed to add orphan vote: %s\n", exception.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
321
src/governance.h
321
src/governance.h
@ -14,6 +14,7 @@
|
||||
#include "key.h"
|
||||
#include "util.h"
|
||||
#include "base58.h"
|
||||
#include "governance-object.h"
|
||||
#include "masternode.h"
|
||||
#include "governance-exceptions.h"
|
||||
#include "governance-vote.h"
|
||||
@ -34,31 +35,11 @@ class CGovernanceTriggerManager;
|
||||
class CGovernanceObject;
|
||||
class CGovernanceVote;
|
||||
|
||||
static const int MAX_GOVERNANCE_OBJECT_DATA_SIZE = 16 * 1024;
|
||||
static const int MIN_GOVERNANCE_PEER_PROTO_VERSION = 70203;
|
||||
|
||||
static const int GOVERNANCE_OBJECT_UNKNOWN = 0;
|
||||
static const int GOVERNANCE_OBJECT_PROPOSAL = 1;
|
||||
static const int GOVERNANCE_OBJECT_TRIGGER = 2;
|
||||
static const int GOVERNANCE_OBJECT_WATCHDOG = 3;
|
||||
|
||||
static const CAmount GOVERNANCE_PROPOSAL_FEE_TX = (0.33*COIN);
|
||||
|
||||
static const int64_t GOVERNANCE_FEE_CONFIRMATIONS = 6;
|
||||
static const int64_t GOVERNANCE_UPDATE_MIN = 60*60;
|
||||
static const int64_t GOVERNANCE_DELETION_DELAY = 10*60;
|
||||
|
||||
|
||||
// FOR SEEN MAP ARRAYS - GOVERNANCE OBJECTS AND VOTES
|
||||
static const int SEEN_OBJECT_IS_VALID = 0;
|
||||
static const int SEEN_OBJECT_ERROR_INVALID = 1;
|
||||
static const int SEEN_OBJECT_ERROR_IMMATURE = 2;
|
||||
static const int SEEN_OBJECT_EXECUTED = 3; //used for triggers
|
||||
static const int SEEN_OBJECT_UNKNOWN = 4; // the default
|
||||
|
||||
extern std::map<uint256, int64_t> mapAskedForGovernanceObject;
|
||||
extern CGovernanceManager governance;
|
||||
|
||||
typedef std::pair<CGovernanceObject, int64_t> object_time_pair_t;
|
||||
|
||||
//
|
||||
// Governance Manager : Contains all proposals for the budget
|
||||
//
|
||||
@ -67,7 +48,6 @@ class CGovernanceManager
|
||||
friend class CGovernanceObject;
|
||||
|
||||
public: // Types
|
||||
|
||||
typedef std::map<uint256, CGovernanceObject> object_m_t;
|
||||
|
||||
typedef object_m_t::iterator object_m_it;
|
||||
@ -90,7 +70,7 @@ public: // Types
|
||||
|
||||
typedef CacheMap<uint256, CGovernanceVote> vote_cache_t;
|
||||
|
||||
typedef CacheMultiMap<uint256, CGovernanceVote> vote_mcache_t;
|
||||
typedef CacheMultiMap<uint256, vote_time_pair_t> vote_mcache_t;
|
||||
|
||||
typedef object_m_t::size_type size_type;
|
||||
|
||||
@ -106,6 +86,12 @@ public: // Types
|
||||
|
||||
typedef hash_s_t::const_iterator hash_s_cit;
|
||||
|
||||
typedef std::map<uint256, object_time_pair_t> object_time_m_t;
|
||||
|
||||
typedef object_time_m_t::iterator object_time_m_it;
|
||||
|
||||
typedef object_time_m_t::const_iterator object_time_m_cit;
|
||||
|
||||
private:
|
||||
static const int MAX_CACHE_SIZE = 1000000;
|
||||
|
||||
@ -122,7 +108,7 @@ private:
|
||||
|
||||
count_m_t mapSeenGovernanceObjects;
|
||||
|
||||
object_m_t mapMasternodeOrphanObjects;
|
||||
object_time_m_t mapMasternodeOrphanObjects;
|
||||
|
||||
object_ref_cache_t mapVoteToObject;
|
||||
|
||||
@ -274,7 +260,7 @@ private:
|
||||
|
||||
void AddOrphanVote(const CGovernanceVote& vote)
|
||||
{
|
||||
mapOrphanVotes.Insert(vote.GetHash(), vote);
|
||||
mapOrphanVotes.Insert(vote.GetHash(), vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME));
|
||||
}
|
||||
|
||||
bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception);
|
||||
@ -300,287 +286,4 @@ private:
|
||||
|
||||
};
|
||||
|
||||
struct vote_instance_t {
|
||||
|
||||
vote_outcome_enum_t eOutcome;
|
||||
int64_t nTime;
|
||||
|
||||
vote_instance_t(vote_outcome_enum_t eOutcomeIn = VOTE_OUTCOME_NONE, int64_t nTimeIn = 0)
|
||||
: eOutcome(eOutcomeIn),
|
||||
nTime(nTimeIn)
|
||||
{}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||
{
|
||||
int nOutcome = int(eOutcome);
|
||||
READWRITE(nOutcome);
|
||||
READWRITE(nTime);
|
||||
if(ser_action.ForRead()) {
|
||||
eOutcome = vote_outcome_enum_t(nOutcome);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<int,vote_instance_t> vote_instance_m_t;
|
||||
|
||||
typedef vote_instance_m_t::iterator vote_instance_m_it;
|
||||
|
||||
typedef vote_instance_m_t::const_iterator vote_instance_m_cit;
|
||||
|
||||
struct vote_rec_t {
|
||||
vote_instance_m_t mapInstances;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||
{
|
||||
READWRITE(mapInstances);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Governance Object
|
||||
*
|
||||
*/
|
||||
|
||||
class CGovernanceObject
|
||||
{
|
||||
friend class CGovernanceManager;
|
||||
|
||||
friend class CGovernanceTriggerManager;
|
||||
|
||||
public: // Types
|
||||
typedef std::map<int, vote_rec_t> vote_m_t;
|
||||
|
||||
typedef vote_m_t::iterator vote_m_it;
|
||||
|
||||
typedef vote_m_t::const_iterator vote_m_cit;
|
||||
|
||||
typedef CacheMultiMap<CTxIn, CGovernanceVote> vote_mcache_t;
|
||||
|
||||
private:
|
||||
/// critical section to protect the inner data structures
|
||||
mutable CCriticalSection cs;
|
||||
|
||||
/// Object typecode
|
||||
int nObjectType;
|
||||
|
||||
/// parent object, 0 is root
|
||||
uint256 nHashParent;
|
||||
|
||||
/// object revision in the system
|
||||
int nRevision;
|
||||
|
||||
/// time this object was created
|
||||
int64_t nTime;
|
||||
|
||||
/// time this object was marked for deletion
|
||||
int64_t nDeletionTime;
|
||||
|
||||
/// fee-tx
|
||||
uint256 nCollateralHash;
|
||||
|
||||
/// Data field - can be used for anything
|
||||
std::string strData;
|
||||
|
||||
/// Masternode info for signed objects
|
||||
CTxIn vinMasternode;
|
||||
std::vector<unsigned char> vchSig;
|
||||
|
||||
/// is valid by blockchain
|
||||
bool fCachedLocalValidity;
|
||||
std::string strLocalValidityError;
|
||||
|
||||
// VARIOUS FLAGS FOR OBJECT / SET VIA MASTERNODE VOTING
|
||||
|
||||
/// true == minimum network support has been reached for this object to be funded (doesn't mean it will for sure though)
|
||||
bool fCachedFunding;
|
||||
|
||||
/// true == minimum network has been reached flagging this object as a valid and understood goverance object (e.g, the serialized data is correct format, etc)
|
||||
bool fCachedValid;
|
||||
|
||||
/// true == minimum network support has been reached saying this object should be deleted from the system entirely
|
||||
bool fCachedDelete;
|
||||
|
||||
/** true == minimum network support has been reached flagging this object as endorsed by an elected representative body
|
||||
* (e.g. business review board / technecial review board /etc)
|
||||
*/
|
||||
bool fCachedEndorsed;
|
||||
|
||||
/// object was updated and cached values should be updated soon
|
||||
bool fDirtyCache;
|
||||
|
||||
/// Object is no longer of interest
|
||||
bool fExpired;
|
||||
|
||||
/// Failed to parse object data
|
||||
bool fUnparsable;
|
||||
|
||||
vote_m_t mapCurrentMNVotes;
|
||||
|
||||
/// Limited map of votes orphaned by MN
|
||||
vote_mcache_t mapOrphanVotes;
|
||||
|
||||
CGovernanceObjectVoteFile fileVotes;
|
||||
|
||||
public:
|
||||
CGovernanceObject();
|
||||
|
||||
CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTime, uint256 nCollateralHashIn, std::string strDataIn);
|
||||
|
||||
CGovernanceObject(const CGovernanceObject& other);
|
||||
|
||||
void swap(CGovernanceObject& first, CGovernanceObject& second); // nothrow
|
||||
|
||||
// Public Getter methods
|
||||
|
||||
int64_t GetCreationTime() const {
|
||||
return nTime;
|
||||
}
|
||||
|
||||
int64_t GetDeletionTime() const {
|
||||
return nDeletionTime;
|
||||
}
|
||||
|
||||
int GetObjectType() const {
|
||||
return nObjectType;
|
||||
}
|
||||
|
||||
const uint256& GetCollateralHash() const {
|
||||
return nCollateralHash;
|
||||
}
|
||||
|
||||
const CTxIn& GetMasternodeVin() const {
|
||||
return vinMasternode;
|
||||
}
|
||||
|
||||
bool IsSetCachedFunding() const {
|
||||
return fCachedFunding;
|
||||
}
|
||||
|
||||
bool IsSetCachedValid() const {
|
||||
return fCachedValid;
|
||||
}
|
||||
|
||||
bool IsSetCachedDelete() const {
|
||||
return fCachedDelete;
|
||||
}
|
||||
|
||||
bool IsSetCachedEndorsed() const {
|
||||
return fCachedEndorsed;
|
||||
}
|
||||
|
||||
bool IsSetDirtyCache() const {
|
||||
return fDirtyCache;
|
||||
}
|
||||
|
||||
bool IsSetExpired() const {
|
||||
return fExpired;
|
||||
}
|
||||
|
||||
void InvalidateVoteCache() {
|
||||
fDirtyCache = true;
|
||||
}
|
||||
|
||||
CGovernanceObjectVoteFile& GetVoteFile() {
|
||||
return fileVotes;
|
||||
}
|
||||
|
||||
// Signature related functions
|
||||
|
||||
void SetMasternodeInfo(const CTxIn& vin);
|
||||
bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode);
|
||||
bool CheckSignature(CPubKey& pubKeyMasternode);
|
||||
|
||||
// CORE OBJECT FUNCTIONS
|
||||
|
||||
bool IsValidLocally(const CBlockIndex* pindex, std::string& strError, bool fCheckCollateral);
|
||||
|
||||
bool IsValidLocally(const CBlockIndex* pindex, std::string& strError, bool& fMissingMasternode, bool fCheckCollateral);
|
||||
|
||||
/// Check the collateral transaction for the budget proposal/finalized budget
|
||||
bool IsCollateralValid(std::string& strError);
|
||||
|
||||
void UpdateLocalValidity(const CBlockIndex *pCurrentBlockIndex);
|
||||
|
||||
void UpdateSentinelVariables(const CBlockIndex *pCurrentBlockIndex);
|
||||
|
||||
int GetObjectSubtype();
|
||||
|
||||
CAmount GetMinCollateralFee();
|
||||
|
||||
UniValue GetJSONObject();
|
||||
|
||||
void Relay();
|
||||
|
||||
uint256 GetHash();
|
||||
|
||||
// GET VOTE COUNT FOR SIGNAL
|
||||
|
||||
int CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const;
|
||||
|
||||
int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetYesCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetNoCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetAbstainCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
|
||||
bool GetCurrentMNVotes(const CTxIn& mnCollateralOutpoint, vote_rec_t& voteRecord);
|
||||
|
||||
// FUNCTIONS FOR DEALING WITH DATA STRING
|
||||
|
||||
std::string GetDataAsHex();
|
||||
std::string GetDataAsString();
|
||||
|
||||
// SERIALIZER
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||
{
|
||||
// SERIALIZE DATA FOR SAVING/LOADING OR NETWORK FUNCTIONS
|
||||
|
||||
READWRITE(nHashParent);
|
||||
READWRITE(nRevision);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nCollateralHash);
|
||||
READWRITE(LIMITED_STRING(strData, MAX_GOVERNANCE_OBJECT_DATA_SIZE));
|
||||
READWRITE(nObjectType);
|
||||
READWRITE(vinMasternode);
|
||||
READWRITE(vchSig);
|
||||
if(nType & SER_DISK) {
|
||||
// Only include these for the disk file format
|
||||
LogPrint("gobject", "CGovernanceObject::SerializationOp Reading/writing votes from/to disk\n");
|
||||
READWRITE(mapCurrentMNVotes);
|
||||
READWRITE(fileVotes);
|
||||
LogPrint("gobject", "CGovernanceObject::SerializationOp hash = %s, vote count = %d\n", GetHash().ToString(), fileVotes.GetVoteCount());
|
||||
}
|
||||
|
||||
// AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY
|
||||
}
|
||||
|
||||
private:
|
||||
// FUNCTIONS FOR DEALING WITH DATA STRING
|
||||
void LoadData();
|
||||
void GetData(UniValue& objResult);
|
||||
|
||||
bool ProcessVote(CNode* pfrom,
|
||||
const CGovernanceVote& vote,
|
||||
CGovernanceException& exception);
|
||||
|
||||
void RebuildVoteMap();
|
||||
|
||||
/// Called when MN's which have voted on this object have been removed
|
||||
void ClearMasternodeVotes();
|
||||
|
||||
void CheckOrphanVotes();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user