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"
# 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 ,
CGovernanceException & exception )
{
int nMNIndex = governance . GetMasternodeIndex ( vote . GetVinMasternode ( ) ) ;
if ( nMNIndex < 0 ) {
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 ) ;
if ( mapOrphanVotes . Insert ( vote . GetVinMasternode ( ) , vote_time_pair_t ( vote , GetAdjustedTime ( ) + GOVERNANCE_ORPHAN_EXPIRATION_TIME ) ) ) {
if ( pfrom ) {
mnodeman . AskForMN ( pfrom , vote . GetVinMasternode ( ) ) ;
}
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 ;
}
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 ;
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 ;
}
2016-11-24 19:12:05 +01:00
int64_t nNow = GetTime ( ) ;
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 "
2016-11-24 19:12:05 +01:00
< < " , MN outpoint = " < < vote . GetVinMasternode ( ) . prevout . ToStringShort ( )
< < " , 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 "
2016-11-24 19:12:05 +01:00
< < " , MN outpoint = " < < vote . GetVinMasternode ( ) . prevout . ToStringShort ( )
< < " , 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-04-02 21:58:54 +02:00
if ( ! mnodeman . AddGovernanceVote ( vote . GetVinMasternode ( ) , vote . GetParentHash ( ) ) ) {
std : : ostringstream ostr ;
2017-06-06 01:47:23 +02:00
ostr < < " CGovernanceObject::ProcessVote -- Unable to add governance vote "
2017-04-02 21:58:54 +02:00
< < " , MN outpoint = " < < vote . GetVinMasternode ( ) . prevout . 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 : : 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 ;
}
}
}
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 ;
}
2016-11-24 19:12:05 +01:00
void CGovernanceObject : : SetMasternodeInfo ( const CTxIn & vin )
{
vinMasternode = vin ;
}
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 ( ) ;
masternode_info_t infoMn = mnodeman . GetMasternodeInfo ( vinMasternode ) ;
if ( ! infoMn . fInfoValid ) {
2017-07-13 11:38:00 +02:00
CMasternode : : CollateralStatus err = CMasternode : : CheckCollateral ( GetMasternodeVin ( ) ) ;
if ( err = = CMasternode : : COLLATERAL_OK ) {
fMissingMasternode = true ;
strError = " Masternode not found: " + strOutpoint ;
} else if ( err = = CMasternode : : COLLATERAL_UTXO_NOT_FOUND ) {
strError = " Failed to find Masternode UTXO, missing masternode= " + GetMasternodeVin ( ) . prevout . ToStringShort ( ) + " \n " ;
} else if ( err = = CMasternode : : COLLATERAL_INVALID_AMOUNT ) {
strError = " Masternode UTXO should have 1000 DASH, missing masternode= " + GetMasternodeVin ( ) . prevout . ToStringShort ( ) + " \n " ;
}
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 ; ) ;
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
2017-07-13 11:38:00 +02:00
AssertLockHeld ( cs_main ) ;
2016-11-24 19:12:05 +01:00
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 ;
}
}
}
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 ) ;
}
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 ( ) ) ;
Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537)
* net: move CBanDB and CAddrDB out of net.h/cpp
This will eventually solve a circular dependency
* net: Create CConnman to encapsulate p2p connections
* net: Move socket binding into CConnman
* net: move OpenNetworkConnection into CConnman
* net: move ban and addrman functions into CConnman
* net: Add oneshot functions to CConnman
* net: move added node functions to CConnman
* net: Add most functions needed for vNodes to CConnman
* net: handle nodesignals in CConnman
* net: Pass CConnection to wallet rather than using the global
* net: Add rpc error for missing/disabled p2p functionality
* net: Pass CConnman around as needed
* gui: add NodeID to the peer table
* net: create generic functor accessors and move vNodes to CConnman
* net: move whitelist functions into CConnman
* net: move nLastNodeId to CConnman
* net: move nLocalHostNonce to CConnman
This behavior seems to have been quite racy and broken.
Move nLocalHostNonce into CNode, and check received nonces against all
non-fully-connected nodes. If there's a match, assume we've connected
to ourself.
* net: move messageHandlerCondition to CConnman
* net: move send/recv statistics to CConnman
* net: move SendBufferSize/ReceiveFloodSize to CConnman
* net: move nLocalServices/nRelevantServices to CConnman
These are in-turn passed to CNode at connection time. This allows us to offer
different services to different peers (or test the effects of doing so).
* net: move semOutbound and semMasternodeOutbound to CConnman
* net: SocketSendData returns written size
* net: move max/max-outbound to CConnman
* net: Pass best block known height into CConnman
CConnman then passes the current best height into CNode at creation time.
This way CConnman/CNode have no dependency on main for height, and the signals
only move in one direction.
This also helps to prevent identity leakage a tiny bit. Before this change, an
attacker could theoretically make 2 connections on different interfaces. They
would connect fully on one, and only establish the initial connection on the
other. Once they receive a new block, they would relay it to your first
connection, and immediately commence the version handshake on the second. Since
the new block height is reflected immediately, they could attempt to learn
whether the two connections were correlated.
This is, of course, incredibly unlikely to work due to the small timings
involved and receipt from other senders. But it doesn't hurt to lock-in
nBestHeight at the time of connection, rather than letting the remote choose
the time.
* net: pass CClientUIInterface into CConnman
* net: Drop StartNode/StopNode and use CConnman directly
* net: Introduce CConnection::Options to avoid passing so many params
* net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options
* net: move vNodesDisconnected into CConnman
* Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting
* Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead
* net: move MAX_FEELER_CONNECTIONS into connman
2017-07-21 11:35:19 +02:00
g_connman - > RelayInv ( inv , PROTOCOL_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 ) ;
}
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 {
2016-12-15 17:27:09 +01:00
vote . Relay ( ) ;
2016-11-24 19:12:05 +01:00
fRemove = true ;
}
+ + it ;
if ( fRemove ) {
mapOrphanVotes . Erase ( key , pairVote ) ;
}
}
}