dash/src/governance-object.cpp

774 lines
26 KiB
C++
Raw Normal View History

2016-12-20 14:26:45 +01:00
// 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 "governance-validators.h"
#include "instantx.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "messagesigner.h"
#include "util.h"
#include <univalue.h>
#include <string>
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,
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
CGovernanceException& exception,
CConnman& connman)
{
LOCK(cs);
A pack of small fixes (#1992) * Make sure gobject collateral was mined `CGovernanceObject::IsCollateralValid()` would still fail for non-mined collateral later trying to check confirmations * Fix powLimit for mainnet/testnet That's a legacy thing, slightly rising it to match the actual bit-shifted value has no effect because real values are already lower. Also clarify values for all networks in comments. * Check for script addresses in CSuperblock::ParsePaymentSchedule() Sentinel should already be downvoting such triggers if they would exist, no need to store/relay them. * Do not process already known valid vote twice in CGovernanceObject::ProcessVote() This should be handled by `CGovernanceManager::ProcessVote()` but imo it's better to have this at the `CGovernanceObject::ProcessVote()` level as well. * Make sure CGovernanceObjectVoteFile::AddVote() never adds/updates already known votes The way `CGovernanceObjectVoteFile::AddVote()` is used (i.e. wrapped in `CGovernanceObjectVoteFile::HasVote()` condition) it's already the case. Hoever nothing would guarantee consistency if it would be used elsewhere without such wrapper, so it's better to have similar check inside. * Do not even try mnb recovery when -connect is set `CConnman::ThreadOpenMasternodeConnections()` thread won't even start when `-connect` is set, so no need to ask for recovery, there is nothing that is going to be able to process such request. * No need for SIGHASH_ANYONECANPAY in PS collateral signature Collateral is just a normal tx created and signed by each participant individually, there is no need for special sig types. * Release semMasternodeOutbound in CConnman::Interrupt() Re-align Dash code with Bitcoin.
2018-03-19 14:08:32 +01:00
// do not process already known valid votes twice
if (fileVotes.HasVote(vote.GetHash())) {
// nothing to do here, not an error
std::ostringstream ostr;
ostr << "CGovernanceObject::ProcessVote -- Already known valid vote";
LogPrint("gobject", "%s\n", ostr.str());
exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_NONE);
return false;
}
if(!mnodeman.Has(vote.GetMasternodeOutpoint())) {
std::ostringstream ostr;
ostr << "CGovernanceObject::ProcessVote -- Masternode " << vote.GetMasternodeOutpoint().ToStringShort() << " 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) {
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
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.emplace(vote_m_t::value_type(vote.GetMasternodeOutpoint(), vote_rec_t())).first;
vote_rec_t& voteRecordRef = 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 = voteRecordRef.mapInstances.emplace(vote_instance_m_t::value_type(int(eSignal), vote_instance_t())).first;
vote_instance_t& voteInstanceRef = it2->second;
// Reject obsolete votes
if(vote.GetTimestamp() < voteInstanceRef.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 = voteInstanceRef.nTime;
if(governance.AreRateChecksEnabled()) {
int64_t nTimeDelta = nNow - voteInstanceRef.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;
}
}
bool onlyOwnerAllowed = nObjectType == GOVERNANCE_OBJECT_PROPOSAL;
// Finally check that the vote is actually valid (done last because of cost of signature verification)
if(!vote.IsValid(onlyOwnerAllowed)) {
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;
}
voteInstanceRef = vote_instance_t(vote.GetOutcome(), nVoteTimeUpdate, vote.GetTimestamp());
A pack of small fixes (#1992) * Make sure gobject collateral was mined `CGovernanceObject::IsCollateralValid()` would still fail for non-mined collateral later trying to check confirmations * Fix powLimit for mainnet/testnet That's a legacy thing, slightly rising it to match the actual bit-shifted value has no effect because real values are already lower. Also clarify values for all networks in comments. * Check for script addresses in CSuperblock::ParsePaymentSchedule() Sentinel should already be downvoting such triggers if they would exist, no need to store/relay them. * Do not process already known valid vote twice in CGovernanceObject::ProcessVote() This should be handled by `CGovernanceManager::ProcessVote()` but imo it's better to have this at the `CGovernanceObject::ProcessVote()` level as well. * Make sure CGovernanceObjectVoteFile::AddVote() never adds/updates already known votes The way `CGovernanceObjectVoteFile::AddVote()` is used (i.e. wrapped in `CGovernanceObjectVoteFile::HasVote()` condition) it's already the case. Hoever nothing would guarantee consistency if it would be used elsewhere without such wrapper, so it's better to have similar check inside. * Do not even try mnb recovery when -connect is set `CConnman::ThreadOpenMasternodeConnections()` thread won't even start when `-connect` is set, so no need to ask for recovery, there is nothing that is going to be able to process such request. * No need for SIGHASH_ANYONECANPAY in PS collateral signature Collateral is just a normal tx created and signed by each participant individually, there is no need for special sig types. * Release semMasternodeOutbound in CConnman::Interrupt() Re-align Dash code with Bitcoin.
2018-03-19 14:08:32 +01:00
fileVotes.AddVote(vote);
fDirtyCache = true;
return true;
}
void CGovernanceObject::ClearMasternodeVotes()
{
LOCK(cs);
vote_m_it it = mapCurrentMNVotes.begin();
while(it != mapCurrentMNVotes.end()) {
2017-08-25 14:57:19 +02:00
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() + "|" +
std::to_string(nRevision) + "|" +
std::to_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& key, const CKeyID& keyID)
{
std::string strError;
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
uint256 hash = GetSignatureHash();
if (!CHashSigner::SignHash(hash, key, vchSig)) {
LogPrintf("CGovernanceObject::Sign -- SignHash() failed\n");
return false;
}
if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) {
LogPrintf("CGovernanceObject::Sign -- VerifyHash() failed, error: %s\n", strError);
return false;
}
} else {
std::string strMessage = GetSignatureMessage();
if (!CMessageSigner::SignMessage(strMessage, vchSig, key)) {
LogPrintf("CGovernanceObject::Sign -- SignMessage() failed\n");
return false;
}
if(!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) {
LogPrintf("CGovernanceObject::Sign -- VerifyMessage() failed, error: %s\n", strError);
return false;
}
}
LogPrint("gobject", "CGovernanceObject::Sign -- pubkey id = %s, masternode = %s\n",
keyID.ToString(), masternodeOutpoint.ToStringShort());
return true;
}
bool CGovernanceObject::CheckSignature(const CKeyID& keyID) const
{
std::string strError;
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
uint256 hash = GetSignatureHash();
if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) {
// could be an old object
std::string strMessage = GetSignatureMessage();
if(!CMessageSigner::VerifyMessage(keyID, 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(keyID, 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);
if (objResult.isObject()) {
obj = objResult;
} else {
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);
2018-02-21 21:24:28 +01:00
DBG( std::cout << "CGovernanceObject::LoadData GetDataAsPlainString = "
<< GetDataAsPlainString()
2018-02-21 21:24:28 +01:00
<< std::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();
2018-02-21 21:24:28 +01:00
DBG( std::cout << ostr.str() << std::endl; );
LogPrintf("%s\n", ostr.str());
return;
}
catch(...) {
fUnparsable = true;
std::ostringstream ostr;
ostr << "CGovernanceObject::LoadData Unknown Error parsing JSON";
2018-02-21 21:24:28 +01:00
DBG( std::cout << ostr.str() << std::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) const
{
bool fMissingMasternode = false;
bool fMissingConfirmations = false;
return IsValidLocally(strError, fMissingMasternode, fMissingConfirmations, fCheckCollateral);
}
bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingMasternode, bool& fMissingConfirmations, bool fCheckCollateral) const
{
fMissingMasternode = false;
fMissingConfirmations = false;
if (fUnparsable) {
strError = "Object data unparseable";
return false;
}
switch(nObjectType) {
case GOVERNANCE_OBJECT_WATCHDOG: {
// watchdogs are deprecated
return false;
}
case GOVERNANCE_OBJECT_PROPOSAL: {
CProposalValidator validator(GetDataAsHexString());
// Note: It's ok to have expired proposals
// they are going to be cleared by CGovernanceManager::UpdateCachesAndClean()
// TODO: should they be tagged as "expired" to skip vote downloading?
if (!validator.Validate(false)) {
strError = strprintf("Invalid proposal data, error messages: %s", validator.GetErrorMessages());
return false;
}
if (fCheckCollateral && !IsCollateralValid(strError, fMissingConfirmations)) {
strError = "Invalid proposal collateral";
return false;
}
return true;
}
case GOVERNANCE_OBJECT_TRIGGER: {
if (!fCheckCollateral)
// nothing else we can check here (yet?)
return true;
std::string strOutpoint = masternodeOutpoint.ToStringShort();
masternode_info_t infoMn;
if (!mnodeman.GetMasternodeInfo(masternodeOutpoint, infoMn)) {
CMasternode::CollateralStatus err = CMasternode::CheckCollateral(masternodeOutpoint, CKeyID());
if (err == CMasternode::COLLATERAL_UTXO_NOT_FOUND) {
strError = "Failed to find Masternode UTXO, missing masternode=" + strOutpoint + "\n";
} else if (err == CMasternode::COLLATERAL_UTXO_NOT_PROTX) {
strError = "Masternode UTXO is not a ProTx, 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.keyIDOperator)) {
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey id = " + infoMn.keyIDOperator.ToString();
return false;
}
return true;
}
default: {
strError = strprintf("Invalid object type %d", nObjectType);
return false;
}
}
}
CAmount CGovernanceObject::GetMinCollateralFee() const
{
// 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) const
{
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", nCollateralHash.ToString());
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
return false;
}
A pack of small fixes (#1992) * Make sure gobject collateral was mined `CGovernanceObject::IsCollateralValid()` would still fail for non-mined collateral later trying to check confirmations * Fix powLimit for mainnet/testnet That's a legacy thing, slightly rising it to match the actual bit-shifted value has no effect because real values are already lower. Also clarify values for all networks in comments. * Check for script addresses in CSuperblock::ParsePaymentSchedule() Sentinel should already be downvoting such triggers if they would exist, no need to store/relay them. * Do not process already known valid vote twice in CGovernanceObject::ProcessVote() This should be handled by `CGovernanceManager::ProcessVote()` but imo it's better to have this at the `CGovernanceObject::ProcessVote()` level as well. * Make sure CGovernanceObjectVoteFile::AddVote() never adds/updates already known votes The way `CGovernanceObjectVoteFile::AddVote()` is used (i.e. wrapped in `CGovernanceObjectVoteFile::HasVote()` condition) it's already the case. Hoever nothing would guarantee consistency if it would be used elsewhere without such wrapper, so it's better to have similar check inside. * Do not even try mnb recovery when -connect is set `CConnman::ThreadOpenMasternodeConnections()` thread won't even start when `-connect` is set, so no need to ask for recovery, there is nothing that is going to be able to process such request. * No need for SIGHASH_ANYONECANPAY in PS collateral signature Collateral is just a normal tx created and signed by each participant individually, there is no need for special sig types. * Release semMasternodeOutbound in CConnman::Interrupt() Re-align Dash code with Bitcoin.
2018-03-19 14:08:32 +01:00
if(nBlockHash == uint256()) {
strError = strprintf("Collateral tx %s is not mined yet", 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);
2018-02-21 21:24:28 +01:00
DBG( std::cout << "IsCollateralValid: txCollateral->vout.size() = " << txCollateral->vout.size() << std::endl; );
2018-02-21 21:24:28 +01:00
DBG( std::cout << "IsCollateralValid: findScript = " << ScriptToAsmStr( findScript, false ) << std::endl; );
2018-02-21 21:24:28 +01:00
DBG( std::cout << "IsCollateralValid: nMinFee = " << nMinFee << std::endl; );
bool foundOpReturn = false;
for (const auto& output : txCollateral->vout) {
2018-02-21 21:24:28 +01:00
DBG( std::cout << "IsCollateralValid txout : " << output.ToString()
<< ", output.nValue = " << output.nValue
<< ", output.scriptPubKey = " << ScriptToAsmStr( output.scriptPubKey, false )
2018-02-21 21:24:28 +01:00
<< std::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) {
2018-02-21 21:24:28 +01:00
DBG( std::cout << "IsCollateralValid foundOpReturn = true" << std::endl; );
foundOpReturn = true;
}
else {
2018-02-21 21:24:28 +01:00
DBG( std::cout << "IsCollateralValid No match, continuing" << std::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 = 0;
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) &&
(!instantsend.IsLockedInstantSendTransaction(nCollateralHash))){
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
{
LOCK(cs);
int nCount = 0;
for (const auto& votepair : mapCurrentMNVotes) {
const vote_rec_t& recVote = votepair.second;
vote_instance_m_cit it2 = recVote.mapInstances.find(eVoteSignalIn);
if(it2 != recVote.mapInstances.end() && it2->second.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) const
{
LOCK(cs);
vote_m_cit it = mapCurrentMNVotes.find(mnCollateralOutpoint);
if (it == mapCurrentMNVotes.end()) {
return false;
}
voteRecord = it->second;
return true;
}
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
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
int nAbsVoteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, nMnCount / 10);
int nAbsDeleteReq = std::max(Params().GetConsensus().nGovernanceMinQuorum, (2 * nMnCount) / 3);
// 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;
}
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
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(nullptr, vote, exception, connman)) {
LogPrintf("CGovernanceObject::CheckOrphanVotes -- Failed to add orphan vote: %s\n", exception.what());
}
else {
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
vote.Relay(connman);
fRemove = true;
}
++it;
if(fRemove) {
cmmapOrphanVotes.Erase(key, pairVote);
}
}
}