2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 The Dash Core developers
2016-11-24 19:12:05 +01:00
// 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"
2016-12-20 14:27:59 +01:00
# include "governance-classes.h"
2016-11-24 19:12:05 +01:00
# include "governance-object.h"
# include "governance-vote.h"
2017-08-25 14:56:48 +02:00
# include "instantx.h"
2017-12-14 01:33:58 +01:00
# include "masternode-sync.h"
2016-11-24 19:12:05 +01:00
# include "masternodeman.h"
2017-04-12 09:04:06 +02:00
# include "messagesigner.h"
2016-11-24 19:12:05 +01:00
# include "util.h"
2016-12-20 14:27:59 +01:00
2016-11-24 19:12:05 +01:00
# 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 ,
2017-09-19 16:51:38 +02:00
CGovernanceException & exception ,
CConnman & connman )
2016-11-24 19:12:05 +01:00
{
2017-09-11 16:13:48 +02:00
if ( ! mnodeman . Has ( vote . GetMasternodeOutpoint ( ) ) ) {
2016-11-24 19:12:05 +01:00
std : : ostringstream ostr ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Masternode index not found " ;
2016-11-24 19:12:05 +01:00
exception = CGovernanceException ( ostr . str ( ) , GOVERNANCE_EXCEPTION_WARNING ) ;
2017-09-11 16:13:48 +02:00
if ( mapOrphanVotes . Insert ( vote . GetMasternodeOutpoint ( ) , vote_time_pair_t ( vote , GetAdjustedTime ( ) + GOVERNANCE_ORPHAN_EXPIRATION_TIME ) ) ) {
2016-11-24 19:12:05 +01:00
if ( pfrom ) {
2017-09-19 16:51:38 +02:00
mnodeman . AskForMN ( pfrom , vote . GetMasternodeOutpoint ( ) , connman ) ;
2016-11-24 19:12:05 +01:00
}
2017-06-06 01:47:23 +02:00
LogPrintf ( " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
}
else {
2017-06-06 01:47:23 +02:00
LogPrint ( " gobject " , " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
}
return false ;
}
2017-09-11 16:13:48 +02:00
vote_m_it it = mapCurrentMNVotes . find ( vote . GetMasternodeOutpoint ( ) ) ;
2016-11-24 19:12:05 +01:00
if ( it = = mapCurrentMNVotes . end ( ) ) {
2017-09-11 16:13:48 +02:00
it = mapCurrentMNVotes . insert ( vote_m_t : : value_type ( vote . GetMasternodeOutpoint ( ) , vote_rec_t ( ) ) ) . first ;
2016-11-24 19:12:05 +01:00
}
vote_rec_t & recVote = it - > second ;
vote_signal_enum_t eSignal = vote . GetSignal ( ) ;
if ( eSignal = = VOTE_SIGNAL_NONE ) {
std : : ostringstream ostr ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Vote signal: none " ;
LogPrint ( " gobject " , " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
exception = CGovernanceException ( ostr . str ( ) , GOVERNANCE_EXCEPTION_WARNING ) ;
return false ;
}
if ( eSignal > MAX_SUPPORTED_VOTE_SIGNAL ) {
std : : ostringstream ostr ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Unsupported vote signal: " < < CGovernanceVoting : : ConvertSignalToString ( vote . GetSignal ( ) ) ;
LogPrintf ( " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
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 ;
2017-02-23 13:29:00 +01:00
// Reject obsolete votes
if ( vote . GetTimestamp ( ) < voteInstance . nCreationTime ) {
std : : ostringstream ostr ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Obsolete vote " ;
LogPrint ( " gobject " , " %s \n " , ostr . str ( ) ) ;
2017-02-23 13:29:00 +01:00
exception = CGovernanceException ( ostr . str ( ) , GOVERNANCE_EXCEPTION_NONE ) ;
return false ;
}
2017-08-29 01:51:44 +02:00
int64_t nNow = GetAdjustedTime ( ) ;
2016-12-08 21:00:49 +01:00
int64_t nVoteTimeUpdate = voteInstance . nTime ;
2016-11-24 19:12:05 +01:00
if ( governance . AreRateChecksEnabled ( ) ) {
int64_t nTimeDelta = nNow - voteInstance . nTime ;
if ( nTimeDelta < GOVERNANCE_UPDATE_MIN ) {
std : : ostringstream ostr ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Masternode voting too often "
2017-09-11 16:13:48 +02:00
< < " , MN outpoint = " < < vote . GetMasternodeOutpoint ( ) . ToStringShort ( )
2016-11-24 19:12:05 +01:00
< < " , governance object hash = " < < GetHash ( ) . ToString ( )
2017-06-06 01:47:23 +02:00
< < " , time delta = " < < nTimeDelta ;
LogPrint ( " gobject " , " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
exception = CGovernanceException ( ostr . str ( ) , GOVERNANCE_EXCEPTION_TEMPORARY_ERROR ) ;
2016-12-08 21:00:49 +01:00
nVoteTimeUpdate = nNow ;
2016-11-24 19:12:05 +01:00
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 ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Invalid vote "
2017-09-11 16:13:48 +02:00
< < " , MN outpoint = " < < vote . GetMasternodeOutpoint ( ) . ToStringShort ( )
2016-11-24 19:12:05 +01:00
< < " , governance object hash = " < < GetHash ( ) . ToString ( )
2017-06-06 01:47:23 +02:00
< < " , vote hash = " < < vote . GetHash ( ) . ToString ( ) ;
LogPrintf ( " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
exception = CGovernanceException ( ostr . str ( ) , GOVERNANCE_EXCEPTION_PERMANENT_ERROR , 20 ) ;
governance . AddInvalidVote ( vote ) ;
return false ;
}
2017-09-11 16:13:48 +02:00
if ( ! mnodeman . AddGovernanceVote ( vote . GetMasternodeOutpoint ( ) , vote . GetParentHash ( ) ) ) {
2017-04-02 21:58:54 +02:00
std : : ostringstream ostr ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Unable to add governance vote "
2017-09-11 16:13:48 +02:00
< < " , MN outpoint = " < < vote . GetMasternodeOutpoint ( ) . ToStringShort ( )
2017-06-06 01:47:23 +02:00
< < " , governance object hash = " < < GetHash ( ) . ToString ( ) ;
LogPrint ( " gobject " , " %s \n " , ostr . str ( ) ) ;
2017-04-02 21:58:54 +02:00
exception = CGovernanceException ( ostr . str ( ) , GOVERNANCE_EXCEPTION_PERMANENT_ERROR ) ;
return false ;
}
2017-02-23 13:29:00 +01:00
voteInstance = vote_instance_t ( vote . GetOutcome ( ) , nVoteTimeUpdate , vote . GetTimestamp ( ) ) ;
2017-04-03 22:06:33 +02:00
if ( ! fileVotes . HasVote ( vote . GetHash ( ) ) ) {
fileVotes . AddVote ( vote ) ;
}
2016-11-24 19:12:05 +01:00
fDirtyCache = true ;
return true ;
}
void CGovernanceObject : : ClearMasternodeVotes ( )
{
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 ) ;
2016-11-24 19:12:05 +01:00
mapCurrentMNVotes . erase ( it + + ) ;
}
else {
+ + it ;
}
}
}
2016-12-20 04:25:20 +01:00
std : : string CGovernanceObject : : GetSignatureMessage ( ) const
{
LOCK ( cs ) ;
std : : string strMessage = nHashParent . ToString ( ) + " | " +
boost : : lexical_cast < std : : string > ( nRevision ) + " | " +
boost : : lexical_cast < std : : string > ( nTime ) + " | " +
strData + " | " +
vinMasternode . prevout . ToStringShort ( ) + " | " +
nCollateralHash . ToString ( ) ;
return strMessage ;
}
2017-09-11 16:13:48 +02:00
void CGovernanceObject : : SetMasternodeVin ( const COutPoint & outpoint )
2016-11-24 19:12:05 +01:00
{
2017-09-11 16:13:48 +02:00
vinMasternode = CTxIn ( outpoint ) ;
2016-11-24 19:12:05 +01:00
}
bool CGovernanceObject : : Sign ( CKey & keyMasternode , CPubKey & pubKeyMasternode )
{
std : : string strError ;
2016-12-20 04:25:20 +01:00
std : : string strMessage = GetSignatureMessage ( ) ;
LOCK ( cs ) ;
2016-11-24 19:12:05 +01:00
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : SignMessage ( strMessage , vchSig , keyMasternode ) ) {
2016-11-24 19:12:05 +01:00
LogPrintf ( " CGovernanceObject::Sign -- SignMessage() failed \n " ) ;
return false ;
}
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : VerifyMessage ( pubKeyMasternode , vchSig , strMessage , strError ) ) {
2016-11-24 19:12:05 +01:00
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 )
{
std : : string strError ;
2016-12-20 04:25:20 +01:00
std : : string strMessage = GetSignatureMessage ( ) ;
LOCK ( cs ) ;
2017-04-12 09:04:06 +02:00
if ( ! CMessageSigner : : VerifyMessage ( pubKeyMasternode , vchSig , strMessage , strError ) ) {
2016-11-24 19:12:05 +01:00
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 ;
}
2016-12-11 07:17:38 +01:00
uint256 CGovernanceObject : : GetHash ( ) const
2016-11-24 19:12:05 +01:00
{
// CREATE HASH OF ALL IMPORTANT PIECES OF DATA
CHashWriter ss ( SER_GETHASH , PROTOCOL_VERSION ) ;
ss < < nHashParent ;
ss < < nRevision ;
ss < < nTime ;
ss < < strData ;
2016-12-20 04:25:20 +01:00
ss < < vinMasternode ;
ss < < vchSig ;
2016-11-24 19:12:05 +01:00
// 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 ; ) ;
2017-06-06 01:47:23 +02:00
LogPrintf ( " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
return ;
}
catch ( . . . ) {
fUnparsable = true ;
std : : ostringstream ostr ;
ostr < < " CGovernanceObject::LoadData Unknown Error parsing JSON " ;
DBG ( cout < < ostr . str ( ) < < endl ; ) ;
2017-06-06 01:47:23 +02:00
LogPrintf ( " %s \n " , ostr . str ( ) ) ;
2016-11-24 19:12:05 +01:00
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 ;
}
2017-01-03 19:32:52 +01:00
void CGovernanceObject : : UpdateLocalValidity ( )
2016-11-24 19:12:05 +01:00
{
2017-07-13 11:38:00 +02:00
LOCK ( cs_main ) ;
2016-11-24 19:12:05 +01:00
// THIS DOES NOT CHECK COLLATERAL, THIS IS CHECKED UPON ORIGINAL ARRIVAL
2017-01-03 19:32:52 +01:00
fCachedLocalValidity = IsValidLocally ( strLocalValidityError , false ) ;
2016-11-24 19:12:05 +01:00
} ;
2017-01-03 19:32:52 +01:00
bool CGovernanceObject : : IsValidLocally ( std : : string & strError , bool fCheckCollateral )
2016-11-24 19:12:05 +01:00
{
bool fMissingMasternode = false ;
2017-07-05 02:31:50 +02:00
bool fMissingConfirmations = false ;
2016-11-24 19:12:05 +01:00
2017-07-05 02:31:50 +02:00
return IsValidLocally ( strError , fMissingMasternode , fMissingConfirmations , fCheckCollateral ) ;
2016-11-24 19:12:05 +01:00
}
2017-07-05 02:31:50 +02:00
bool CGovernanceObject : : IsValidLocally ( std : : string & strError , bool & fMissingMasternode , bool & fMissingConfirmations , bool fCheckCollateral )
2016-11-24 19:12:05 +01:00
{
fMissingMasternode = false ;
2017-07-05 02:31:50 +02:00
fMissingConfirmations = false ;
2016-11-24 19:12:05 +01:00
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
// CHECK COLLATERAL IF REQUIRED (HIGH CPU USAGE)
if ( fCheckCollateral ) {
if ( ( nObjectType = = GOVERNANCE_OBJECT_TRIGGER ) | | ( nObjectType = = GOVERNANCE_OBJECT_WATCHDOG ) ) {
std : : string strOutpoint = vinMasternode . prevout . ToStringShort ( ) ;
2017-09-11 16:13:48 +02:00
masternode_info_t infoMn ;
if ( ! mnodeman . GetMasternodeInfo ( vinMasternode . prevout , infoMn ) ) {
2017-07-13 11:38:00 +02:00
2017-09-11 16:13:48 +02:00
CMasternode : : CollateralStatus err = CMasternode : : CheckCollateral ( vinMasternode . prevout ) ;
2017-07-13 11:38:00 +02:00
if ( err = = CMasternode : : COLLATERAL_OK ) {
fMissingMasternode = true ;
strError = " Masternode not found: " + strOutpoint ;
} else if ( err = = CMasternode : : COLLATERAL_UTXO_NOT_FOUND ) {
2017-09-11 16:13:48 +02:00
strError = " Failed to find Masternode UTXO, missing masternode= " + strOutpoint + " \n " ;
2017-07-13 11:38:00 +02:00
} else if ( err = = CMasternode : : COLLATERAL_INVALID_AMOUNT ) {
2017-09-11 16:13:48 +02:00
strError = " Masternode UTXO should have 1000 DASH, missing masternode= " + strOutpoint + " \n " ;
2017-07-13 11:38:00 +02:00
}
2016-11-24 19:12:05 +01:00
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 ;
}
2017-07-05 02:31:50 +02:00
if ( ! IsCollateralValid ( strError , fMissingConfirmations ) )
2016-11-24 19:12:05 +01:00
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 ;
}
}
2017-07-05 02:31:50 +02:00
bool CGovernanceObject : : IsCollateralValid ( std : : string & strError , bool & fMissingConfirmations )
2016-11-24 19:12:05 +01:00
{
strError = " " ;
2017-07-05 02:31:50 +02:00
fMissingConfirmations = false ;
2016-11-24 19:12:05 +01:00
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 ) ;
2017-07-05 02:31:50 +02:00
DBG ( cout < < " IsCollateralValid: txCollateral.vout.size() = " < < txCollateral . vout . size ( ) < < endl ; ) ;
2016-11-24 19:12:05 +01:00
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 ; ) ;
2017-12-07 02:33:51 +01:00
if ( ! o . scriptPubKey . IsPayToPublicKeyHash ( ) & & ! o . scriptPubKey . IsUnspendable ( ) ) {
2016-11-24 19:12:05 +01:00
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
2017-07-13 11:38:00 +02:00
AssertLockHeld ( cs_main ) ;
2017-08-25 14:56:48 +02:00
int nConfirmationsIn = instantsend . GetConfirmations ( nCollateralHash ) ;
2016-11-24 19:12:05 +01:00
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 ;
}
}
}
2017-07-05 02:31:50 +02:00
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 ) ;
2016-11-24 19:12:05 +01:00
return false ;
}
2017-07-05 02:31:50 +02:00
strError = " valid " ;
2016-11-24 19:12:05 +01:00
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 ) ;
}
2017-09-11 16:13:48 +02:00
bool CGovernanceObject : : GetCurrentMNVotes ( const COutPoint & mnCollateralOutpoint , vote_rec_t & voteRecord )
2016-11-24 19:12:05 +01:00
{
2017-08-25 14:57:19 +02:00
vote_m_it it = mapCurrentMNVotes . find ( mnCollateralOutpoint ) ;
2016-11-24 19:12:05 +01:00
if ( it = = mapCurrentMNVotes . end ( ) ) {
return false ;
}
voteRecord = it - > second ;
return true ;
}
2017-09-19 16:51:38 +02:00
void CGovernanceObject : : Relay ( CConnman & connman )
2016-11-24 19:12:05 +01:00
{
2017-12-14 01:33:58 +01:00
// Do not relay until fully synced
if ( ! masternodeSync . IsSynced ( ) ) {
LogPrint ( " gobject " , " CGovernanceObject::Relay -- won't relay until fully synced \n " ) ;
return ;
}
2016-11-24 19:12:05 +01:00
CInv inv ( MSG_GOVERNANCE_OBJECT , GetHash ( ) ) ;
2017-10-04 22:12:53 +02:00
connman . RelayInv ( inv , MIN_GOVERNANCE_PEER_PROTO_VERSION ) ;
2016-11-24 19:12:05 +01:00
}
2016-12-11 07:17:38 +01:00
void CGovernanceObject : : UpdateSentinelVariables ( )
2016-11-24 19:12:05 +01:00
{
// 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 ) ;
2016-11-25 15:08:48 +01:00
int nAbsDeleteReq = std : : max ( Params ( ) . GetConsensus ( ) . nGovernanceMinQuorum , ( 2 * nMnCount ) / 3 ) ;
2016-11-24 19:12:05 +01:00
// todo - 12.1 - Temporarily set to 1 for testing - reverted
//nAbsVoteReq = 1;
// SET SENTINEL FLAGS TO FALSE
fCachedFunding = false ;
fCachedValid = true ; //default to valid
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 ;
2016-12-14 16:27:46 +01:00
if ( ( GetAbsoluteYesCount ( VOTE_SIGNAL_DELETE ) > = nAbsDeleteReq ) & & ! fCachedDelete ) {
2016-11-24 19:12:05 +01:00
fCachedDelete = true ;
2016-12-14 16:27:46 +01:00
if ( nDeletionTime = = 0 ) {
nDeletionTime = GetAdjustedTime ( ) ;
}
2016-11-24 19:12:05 +01:00
}
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 . 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 ) ;
}
2017-09-19 16:51:38 +02:00
void CGovernanceObject : : CheckOrphanVotes ( CConnman & connman )
2016-11-24 19:12:05 +01:00
{
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 ;
2017-09-11 16:13:48 +02:00
const COutPoint & key = it - > key ;
2016-11-24 19:12:05 +01:00
const vote_time_pair_t & pairVote = it - > value ;
const CGovernanceVote & vote = pairVote . first ;
if ( pairVote . second < nNow ) {
fRemove = true ;
}
2017-09-11 16:13:48 +02:00
else if ( ! mnodeman . Has ( vote . GetMasternodeOutpoint ( ) ) ) {
2016-11-24 19:12:05 +01:00
+ + it ;
continue ;
}
CGovernanceException exception ;
2017-09-19 16:51:38 +02:00
if ( ! ProcessVote ( NULL , vote , exception , connman ) ) {
2016-11-24 19:12:05 +01:00
LogPrintf ( " CGovernanceObject::CheckOrphanVotes -- Failed to add orphan vote: %s \n " , exception . what ( ) ) ;
}
else {
2017-09-19 16:51:38 +02:00
vote . Relay ( connman ) ;
2016-11-24 19:12:05 +01:00
fRemove = true ;
}
+ + it ;
if ( fRemove ) {
mapOrphanVotes . Erase ( key , pairVote ) ;
}
}
}