5b1c4d8a11
* Drop nBudgetProposalEstablishingTime * Refactor: replace `== COutPoint()` with `.IsNull()` * Refactor: add `operator bool()` to CMasternodePing * Refactor: actually use `operator bool()` for CPendingDsaRequest * Refactor: fixing code style in TrafficGraphData * Fix some comments and whitespaces * Drop CGovernanceVote::GetTypeHash() * Drop legacy X11 code No longer used... if it ever was used at all. * Move `<boost/foreach.hpp>` out of coins.h * Simplify CMasternodeBlockPayees::GetRequiredPaymentsString() Also less of boost::lexical_cast * Drop CTxDSIn::nSentTimes * Fix few warnings * fix warning (timer) * fix nit
783 lines
26 KiB
C++
783 lines
26 KiB
C++
// Copyright (c) 2014-2017 The Dash Core developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "core_io.h"
|
|
#include "governance.h"
|
|
#include "governance-classes.h"
|
|
#include "governance-object.h"
|
|
#include "governance-vote.h"
|
|
#include "instantx.h"
|
|
#include "masternode-sync.h"
|
|
#include "masternodeman.h"
|
|
#include "messagesigner.h"
|
|
#include "util.h"
|
|
|
|
#include <univalue.h>
|
|
|
|
CGovernanceObject::CGovernanceObject():
|
|
cs(),
|
|
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
|
nHashParent(),
|
|
nRevision(0),
|
|
nTime(0),
|
|
nDeletionTime(0),
|
|
nCollateralHash(),
|
|
vchData(),
|
|
masternodeOutpoint(),
|
|
vchSig(),
|
|
fCachedLocalValidity(false),
|
|
strLocalValidityError(),
|
|
fCachedFunding(false),
|
|
fCachedValid(true),
|
|
fCachedDelete(false),
|
|
fCachedEndorsed(false),
|
|
fDirtyCache(true),
|
|
fExpired(false),
|
|
fUnparsable(false),
|
|
mapCurrentMNVotes(),
|
|
cmmapOrphanVotes(),
|
|
fileVotes()
|
|
{
|
|
// PARSE JSON DATA STORAGE (VCHDATA)
|
|
LoadData();
|
|
}
|
|
|
|
CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevisionIn, int64_t nTimeIn, const uint256& nCollateralHashIn, const std::string& strDataHexIn):
|
|
cs(),
|
|
nObjectType(GOVERNANCE_OBJECT_UNKNOWN),
|
|
nHashParent(nHashParentIn),
|
|
nRevision(nRevisionIn),
|
|
nTime(nTimeIn),
|
|
nDeletionTime(0),
|
|
nCollateralHash(nCollateralHashIn),
|
|
vchData(ParseHex(strDataHexIn)),
|
|
masternodeOutpoint(),
|
|
vchSig(),
|
|
fCachedLocalValidity(false),
|
|
strLocalValidityError(),
|
|
fCachedFunding(false),
|
|
fCachedValid(true),
|
|
fCachedDelete(false),
|
|
fCachedEndorsed(false),
|
|
fDirtyCache(true),
|
|
fExpired(false),
|
|
fUnparsable(false),
|
|
mapCurrentMNVotes(),
|
|
cmmapOrphanVotes(),
|
|
fileVotes()
|
|
{
|
|
// PARSE JSON DATA STORAGE (VCHDATA)
|
|
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),
|
|
vchData(other.vchData),
|
|
masternodeOutpoint(other.masternodeOutpoint),
|
|
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),
|
|
cmmapOrphanVotes(other.cmmapOrphanVotes),
|
|
fileVotes(other.fileVotes)
|
|
{}
|
|
|
|
bool CGovernanceObject::ProcessVote(CNode* pfrom,
|
|
const CGovernanceVote& vote,
|
|
CGovernanceException& exception,
|
|
CConnman& connman)
|
|
{
|
|
if(!mnodeman.Has(vote.GetMasternodeOutpoint())) {
|
|
std::ostringstream ostr;
|
|
ostr << "CGovernanceObject::ProcessVote -- Masternode index not found";
|
|
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING);
|
|
if(cmmapOrphanVotes.Insert(vote.GetMasternodeOutpoint(), vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) {
|
|
if(pfrom) {
|
|
mnodeman.AskForMN(pfrom, vote.GetMasternodeOutpoint(), connman);
|
|
}
|
|
LogPrintf("%s\n", ostr.str());
|
|
}
|
|
else {
|
|
LogPrint("gobject", "%s\n", ostr.str());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
vote_m_it it = mapCurrentMNVotes.find(vote.GetMasternodeOutpoint());
|
|
if(it == mapCurrentMNVotes.end()) {
|
|
it = mapCurrentMNVotes.insert(vote_m_t::value_type(vote.GetMasternodeOutpoint(), 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";
|
|
LogPrint("gobject", "%s\n", ostr.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());
|
|
LogPrintf("%s\n", ostr.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;
|
|
|
|
// Reject obsolete votes
|
|
if(vote.GetTimestamp() < voteInstance.nCreationTime) {
|
|
std::ostringstream ostr;
|
|
ostr << "CGovernanceObject::ProcessVote -- Obsolete vote";
|
|
LogPrint("gobject", "%s\n", ostr.str());
|
|
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_NONE);
|
|
return false;
|
|
}
|
|
|
|
int64_t nNow = GetAdjustedTime();
|
|
int64_t nVoteTimeUpdate = voteInstance.nTime;
|
|
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.GetMasternodeOutpoint().ToStringShort()
|
|
<< ", governance object hash = " << GetHash().ToString()
|
|
<< ", time delta = " << nTimeDelta;
|
|
LogPrint("gobject", "%s\n", ostr.str());
|
|
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_TEMPORARY_ERROR);
|
|
nVoteTimeUpdate = nNow;
|
|
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.GetMasternodeOutpoint().ToStringShort()
|
|
<< ", governance object hash = " << GetHash().ToString()
|
|
<< ", vote hash = " << vote.GetHash().ToString();
|
|
LogPrintf("%s\n", ostr.str());
|
|
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20);
|
|
governance.AddInvalidVote(vote);
|
|
return false;
|
|
}
|
|
if(!mnodeman.AddGovernanceVote(vote.GetMasternodeOutpoint(), vote.GetParentHash())) {
|
|
std::ostringstream ostr;
|
|
ostr << "CGovernanceObject::ProcessVote -- Unable to add governance vote"
|
|
<< ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort()
|
|
<< ", governance object hash = " << GetHash().ToString();
|
|
LogPrint("gobject", "%s\n", ostr.str());
|
|
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR);
|
|
return false;
|
|
}
|
|
voteInstance = vote_instance_t(vote.GetOutcome(), nVoteTimeUpdate, vote.GetTimestamp());
|
|
if(!fileVotes.HasVote(vote.GetHash())) {
|
|
fileVotes.AddVote(vote);
|
|
}
|
|
fDirtyCache = true;
|
|
return true;
|
|
}
|
|
|
|
void CGovernanceObject::ClearMasternodeVotes()
|
|
{
|
|
vote_m_it it = mapCurrentMNVotes.begin();
|
|
while(it != mapCurrentMNVotes.end()) {
|
|
if(!mnodeman.Has(it->first)) {
|
|
fileVotes.RemoveVotesFromMasternode(it->first);
|
|
mapCurrentMNVotes.erase(it++);
|
|
}
|
|
else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string CGovernanceObject::GetSignatureMessage() const
|
|
{
|
|
LOCK(cs);
|
|
std::string strMessage = nHashParent.ToString() + "|" +
|
|
boost::lexical_cast<std::string>(nRevision) + "|" +
|
|
boost::lexical_cast<std::string>(nTime) + "|" +
|
|
GetDataAsHexString() + "|" +
|
|
masternodeOutpoint.ToStringShort() + "|" +
|
|
nCollateralHash.ToString();
|
|
|
|
return strMessage;
|
|
}
|
|
|
|
uint256 CGovernanceObject::GetHash() const
|
|
{
|
|
// Note: doesn't match serialization
|
|
|
|
// CREATE HASH OF ALL IMPORTANT PIECES OF DATA
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << nHashParent;
|
|
ss << nRevision;
|
|
ss << nTime;
|
|
ss << GetDataAsHexString();
|
|
ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing
|
|
ss << vchSig;
|
|
// fee_tx is left out on purpose
|
|
|
|
DBG( printf("CGovernanceObject::GetHash %i %li %s\n", nRevision, nTime, GetDataAsHexString().c_str()); );
|
|
|
|
return ss.GetHash();
|
|
}
|
|
|
|
uint256 CGovernanceObject::GetSignatureHash() const
|
|
{
|
|
return SerializeHash(*this);
|
|
}
|
|
|
|
void CGovernanceObject::SetMasternodeOutpoint(const COutPoint& outpoint)
|
|
{
|
|
masternodeOutpoint = outpoint;
|
|
}
|
|
|
|
bool CGovernanceObject::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode)
|
|
{
|
|
std::string strError;
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::SignHash(hash, keyMasternode, vchSig)) {
|
|
LogPrintf("CGovernanceObject::Sign -- SignHash() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) {
|
|
LogPrintf("CGovernanceObject::Sign -- VerifyHash() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
} else {
|
|
std::string strMessage = GetSignatureMessage();
|
|
if (!CMessageSigner::SignMessage(strMessage, vchSig, keyMasternode)) {
|
|
LogPrintf("CGovernanceObject::Sign -- SignMessage() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
|
LogPrintf("CGovernanceObject::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogPrint("gobject", "CGovernanceObject::Sign -- pubkey id = %s, masternode = %s\n",
|
|
pubKeyMasternode.GetID().ToString(), masternodeOutpoint.ToStringShort());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGovernanceObject::CheckSignature(const CPubKey& pubKeyMasternode)
|
|
{
|
|
std::string strError;
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) {
|
|
// could be an old object
|
|
std::string strMessage = GetSignatureMessage();
|
|
|
|
if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
|
// nope, not in old format either
|
|
LogPrintf("CGovernance::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
std::string strMessage = GetSignatureMessage();
|
|
|
|
if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) {
|
|
LogPrintf("CGovernance::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Return the actual object from the vchData JSON structure.
|
|
|
|
Returns an empty object on error.
|
|
*/
|
|
UniValue CGovernanceObject::GetJSONObject()
|
|
{
|
|
UniValue obj(UniValue::VOBJ);
|
|
if(vchData.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 vchData
|
|
*
|
|
*/
|
|
|
|
void CGovernanceObject::LoadData()
|
|
{
|
|
// todo : 12.1 - resolved
|
|
//return;
|
|
|
|
if(vchData.empty()) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// ATTEMPT TO LOAD JSON STRING FROM VCHDATA
|
|
UniValue objResult(UniValue::VOBJ);
|
|
GetData(objResult);
|
|
|
|
DBG( cout << "CGovernanceObject::LoadData GetDataAsPlainString = "
|
|
<< GetDataAsPlainString()
|
|
<< 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("%s\n", ostr.str());
|
|
return;
|
|
}
|
|
catch(...) {
|
|
fUnparsable = true;
|
|
std::ostringstream ostr;
|
|
ostr << "CGovernanceObject::LoadData Unknown Error parsing JSON";
|
|
DBG( cout << ostr.str() << endl; );
|
|
LogPrintf("%s\n", ostr.str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GetData - Example usage:
|
|
* --------------------------------------------------------
|
|
*
|
|
* Decode governance object data into UniValue(VOBJ)
|
|
*
|
|
*/
|
|
|
|
void CGovernanceObject::GetData(UniValue& objResult)
|
|
{
|
|
UniValue o(UniValue::VOBJ);
|
|
std::string s = GetDataAsPlainString();
|
|
o.read(s);
|
|
objResult = o;
|
|
}
|
|
|
|
/**
|
|
* GetData - As
|
|
* --------------------------------------------------------
|
|
*
|
|
*/
|
|
|
|
std::string CGovernanceObject::GetDataAsHexString() const
|
|
{
|
|
return HexStr(vchData);
|
|
}
|
|
|
|
std::string CGovernanceObject::GetDataAsPlainString() const
|
|
{
|
|
return std::string(vchData.begin(), vchData.end());
|
|
}
|
|
|
|
void CGovernanceObject::UpdateLocalValidity()
|
|
{
|
|
LOCK(cs_main);
|
|
// THIS DOES NOT CHECK COLLATERAL, THIS IS CHECKED UPON ORIGINAL ARRIVAL
|
|
fCachedLocalValidity = IsValidLocally(strLocalValidityError, false);
|
|
};
|
|
|
|
|
|
bool CGovernanceObject::IsValidLocally(std::string& strError, bool fCheckCollateral)
|
|
{
|
|
bool fMissingMasternode = false;
|
|
bool fMissingConfirmations = false;
|
|
|
|
return IsValidLocally(strError, fMissingMasternode, fMissingConfirmations, fCheckCollateral);
|
|
}
|
|
|
|
bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingMasternode, bool& fMissingConfirmations, bool fCheckCollateral)
|
|
{
|
|
fMissingMasternode = false;
|
|
fMissingConfirmations = false;
|
|
|
|
if(fUnparsable) {
|
|
strError = "Object data unparseable";
|
|
return false;
|
|
}
|
|
|
|
switch(nObjectType) {
|
|
case GOVERNANCE_OBJECT_PROPOSAL:
|
|
case GOVERNANCE_OBJECT_TRIGGER:
|
|
case GOVERNANCE_OBJECT_WATCHDOG:
|
|
if (vchData.size() > MAX_GOVERNANCE_OBJECT_DATA_SIZE) {
|
|
strError = strprintf("Invalid object size %d", vchData.size());
|
|
return false;
|
|
}
|
|
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
|
|
|
|
// CHECK COLLATERAL IF REQUIRED (HIGH CPU USAGE)
|
|
|
|
if(fCheckCollateral) {
|
|
if((nObjectType == GOVERNANCE_OBJECT_TRIGGER) || (nObjectType == GOVERNANCE_OBJECT_WATCHDOG)) {
|
|
std::string strOutpoint = masternodeOutpoint.ToStringShort();
|
|
masternode_info_t infoMn;
|
|
if(!mnodeman.GetMasternodeInfo(masternodeOutpoint, infoMn)) {
|
|
|
|
CMasternode::CollateralStatus err = CMasternode::CheckCollateral(masternodeOutpoint, CPubKey());
|
|
if (err == CMasternode::COLLATERAL_UTXO_NOT_FOUND) {
|
|
strError = "Failed to find Masternode UTXO, missing masternode=" + strOutpoint + "\n";
|
|
} else if (err == CMasternode::COLLATERAL_INVALID_AMOUNT) {
|
|
strError = "Masternode UTXO should have 1000 DASH, missing masternode=" + strOutpoint + "\n";
|
|
} else if (err == CMasternode::COLLATERAL_INVALID_PUBKEY) {
|
|
fMissingMasternode = true;
|
|
strError = "Masternode not found: " + strOutpoint;
|
|
} else if (err == CMasternode::COLLATERAL_OK) {
|
|
// this should never happen with CPubKey() as a param
|
|
strError = "CheckCollateral critical failure! Masternode: " + 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;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!IsCollateralValid(strError, fMissingConfirmations))
|
|
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, bool& fMissingConfirmations)
|
|
{
|
|
strError = "";
|
|
fMissingConfirmations = false;
|
|
CAmount nMinFee = GetMinCollateralFee();
|
|
uint256 nExpectedHash = GetHash();
|
|
|
|
CTransactionRef 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;
|
|
for (const auto& output : txCollateral->vout) {
|
|
DBG( cout << "IsCollateralValid txout : " << output.ToString()
|
|
<< ", output.nValue = " << output.nValue
|
|
<< ", output.scriptPubKey = " << ScriptToAsmStr( output.scriptPubKey, false )
|
|
<< endl; );
|
|
if(!output.scriptPubKey.IsPayToPublicKeyHash() && !output.scriptPubKey.IsUnspendable()) {
|
|
strError = strprintf("Invalid Script %s", txCollateral->ToString());
|
|
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
|
return false;
|
|
}
|
|
if(output.scriptPubKey == findScript && output.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
|
|
|
|
AssertLockHeld(cs_main);
|
|
int nConfirmationsIn = instantsend.GetConfirmations(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 = strprintf("Collateral requires at least %d confirmations to be relayed throughout the network (it has only %d)", GOVERNANCE_FEE_CONFIRMATIONS, nConfirmationsIn);
|
|
if (nConfirmationsIn >= GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS) {
|
|
fMissingConfirmations = true;
|
|
strError += ", pre-accepted -- waiting for required confirmations";
|
|
} else {
|
|
strError += ", rejected -- try again later";
|
|
}
|
|
LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
|
|
|
return false;
|
|
}
|
|
|
|
strError = "valid";
|
|
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 COutPoint& mnCollateralOutpoint, vote_rec_t& voteRecord)
|
|
{
|
|
vote_m_it it = mapCurrentMNVotes.find(mnCollateralOutpoint);
|
|
if (it == mapCurrentMNVotes.end()) {
|
|
return false;
|
|
}
|
|
voteRecord = it->second;
|
|
return true;
|
|
}
|
|
|
|
void CGovernanceObject::Relay(CConnman& connman)
|
|
{
|
|
// Do not relay until fully synced
|
|
if(!masternodeSync.IsSynced()) {
|
|
LogPrint("gobject", "CGovernanceObject::Relay -- won't relay until fully synced\n");
|
|
return;
|
|
}
|
|
|
|
CInv inv(MSG_GOVERNANCE_OBJECT, GetHash());
|
|
connman.RelayInv(inv, MIN_GOVERNANCE_PEER_PROTO_VERSION);
|
|
}
|
|
|
|
void CGovernanceObject::UpdateSentinelVariables()
|
|
{
|
|
// 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);
|
|
int nAbsDeleteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, (2 * nMnCount) / 3);
|
|
// todo - 12.1 - Temporarily set to 1 for testing - reverted
|
|
//nAbsVoteReq = 1;
|
|
|
|
// SET SENTINEL FLAGS TO FALSE
|
|
|
|
fCachedFunding = false;
|
|
fCachedValid = true; //default to valid
|
|
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_DELETE) >= nAbsDeleteReq) && !fCachedDelete) {
|
|
fCachedDelete = true;
|
|
if(nDeletionTime == 0) {
|
|
nDeletionTime = GetAdjustedTime();
|
|
}
|
|
}
|
|
if(GetAbsoluteYesCount(VOTE_SIGNAL_ENDORSED) >= nAbsVoteReq) fCachedEndorsed = true;
|
|
|
|
if(GetAbsoluteNoCount(VOTE_SIGNAL_VALID) >= nAbsVoteReq) fCachedValid = 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.vchData, second.vchData);
|
|
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(CConnman& connman)
|
|
{
|
|
int64_t nNow = GetAdjustedTime();
|
|
const vote_cmm_t::list_t& listVotes = cmmapOrphanVotes.GetItemList();
|
|
vote_cmm_t::list_cit it = listVotes.begin();
|
|
while(it != listVotes.end()) {
|
|
bool fRemove = false;
|
|
const COutPoint& 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.GetMasternodeOutpoint())) {
|
|
++it;
|
|
continue;
|
|
}
|
|
CGovernanceException exception;
|
|
if(!ProcessVote(NULL, vote, exception, connman)) {
|
|
LogPrintf("CGovernanceObject::CheckOrphanVotes -- Failed to add orphan vote: %s\n", exception.what());
|
|
}
|
|
else {
|
|
vote.Relay(connman);
|
|
fRemove = true;
|
|
}
|
|
++it;
|
|
if(fRemove) {
|
|
cmmapOrphanVotes.Erase(key, pairVote);
|
|
}
|
|
}
|
|
}
|