2018-02-14 14:43:03 +01:00
// Copyright (c) 2018 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include "deterministicmns.h"
# include "specialtx.h"
# include "base58.h"
2018-11-06 09:54:23 +01:00
# include "chainparams.h"
2018-02-14 14:43:03 +01:00
# include "core_io.h"
2018-11-06 09:54:23 +01:00
# include "script/standard.h"
2018-02-15 13:57:18 +01:00
# include "spork.h"
2018-11-06 09:54:23 +01:00
# include "validation.h"
# include "validationinterface.h"
2018-02-14 14:43:03 +01:00
2018-11-25 14:27:18 +01:00
# include "llmq/quorums_commitment.h"
# include "llmq/quorums_utils.h"
2018-02-14 14:43:03 +01:00
# include <univalue.h>
static const std : : string DB_LIST_SNAPSHOT = " dmn_S " ;
static const std : : string DB_LIST_DIFF = " dmn_D " ;
CDeterministicMNManager * deterministicMNManager ;
std : : string CDeterministicMNState : : ToString ( ) const
{
CTxDestination dest ;
std : : string payoutAddress = " unknown " ;
std : : string operatorRewardAddress = " none " ;
if ( ExtractDestination ( scriptPayout , dest ) ) {
payoutAddress = CBitcoinAddress ( dest ) . ToString ( ) ;
}
if ( ExtractDestination ( scriptOperatorPayout , dest ) ) {
operatorRewardAddress = CBitcoinAddress ( dest ) . ToString ( ) ;
}
2018-03-19 12:29:59 +01:00
return strprintf ( " CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
2018-11-06 09:54:23 +01:00
" keyIDOwner=%s, pubKeyOperator=%s, keyIDVoting=%s, addr=%s, payoutAddress=%s, operatorRewardAddress=%s) " ,
nRegisteredHeight , nLastPaidHeight , nPoSePenalty , nPoSeRevivedHeight , nPoSeBanHeight , nRevocationReason ,
keyIDOwner . ToString ( ) , pubKeyOperator . ToString ( ) , keyIDVoting . ToString ( ) , addr . ToStringIPPort ( false ) , payoutAddress , operatorRewardAddress ) ;
2018-02-14 14:43:03 +01:00
}
void CDeterministicMNState : : ToJson ( UniValue & obj ) const
{
obj . clear ( ) ;
obj . setObject ( ) ;
obj . push_back ( Pair ( " registeredHeight " , nRegisteredHeight ) ) ;
obj . push_back ( Pair ( " lastPaidHeight " , nLastPaidHeight ) ) ;
2018-08-31 11:33:37 +02:00
obj . push_back ( Pair ( " PoSePenalty " , nPoSePenalty ) ) ;
2018-02-14 14:43:03 +01:00
obj . push_back ( Pair ( " PoSeRevivedHeight " , nPoSeRevivedHeight ) ) ;
obj . push_back ( Pair ( " PoSeBanHeight " , nPoSeBanHeight ) ) ;
2018-03-19 12:29:59 +01:00
obj . push_back ( Pair ( " revocationReason " , nRevocationReason ) ) ;
2018-02-14 14:43:03 +01:00
obj . push_back ( Pair ( " keyIDOwner " , keyIDOwner . ToString ( ) ) ) ;
2018-10-21 21:45:16 +02:00
obj . push_back ( Pair ( " pubKeyOperator " , pubKeyOperator . ToString ( ) ) ) ;
2018-02-14 14:43:03 +01:00
obj . push_back ( Pair ( " keyIDVoting " , keyIDVoting . ToString ( ) ) ) ;
obj . push_back ( Pair ( " addr " , addr . ToStringIPPort ( false ) ) ) ;
CTxDestination dest ;
if ( ExtractDestination ( scriptPayout , dest ) ) {
CBitcoinAddress bitcoinAddress ( dest ) ;
obj . push_back ( Pair ( " payoutAddress " , bitcoinAddress . ToString ( ) ) ) ;
}
if ( ExtractDestination ( scriptOperatorPayout , dest ) ) {
CBitcoinAddress bitcoinAddress ( dest ) ;
obj . push_back ( Pair ( " operatorRewardAddress " , bitcoinAddress . ToString ( ) ) ) ;
}
}
std : : string CDeterministicMN : : ToString ( ) const
{
2018-10-25 16:29:50 +02:00
return strprintf ( " CDeterministicMN(proTxHash=%s, collateralOutpoint=%s, nOperatorReward=%f, state=%s " , proTxHash . ToString ( ) , collateralOutpoint . ToStringShort ( ) , ( double ) nOperatorReward / 100 , pdmnState - > ToString ( ) ) ;
2018-02-14 14:43:03 +01:00
}
void CDeterministicMN : : ToJson ( UniValue & obj ) const
{
obj . clear ( ) ;
obj . setObject ( ) ;
UniValue stateObj ;
pdmnState - > ToJson ( stateObj ) ;
obj . push_back ( Pair ( " proTxHash " , proTxHash . ToString ( ) ) ) ;
2018-10-25 16:29:50 +02:00
obj . push_back ( Pair ( " collateralHash " , collateralOutpoint . hash . ToString ( ) ) ) ;
obj . push_back ( Pair ( " collateralIndex " , ( int ) collateralOutpoint . n ) ) ;
2018-02-14 14:43:03 +01:00
obj . push_back ( Pair ( " operatorReward " , ( double ) nOperatorReward / 100 ) ) ;
obj . push_back ( Pair ( " state " , stateObj ) ) ;
}
bool CDeterministicMNList : : IsMNValid ( const uint256 & proTxHash ) const
{
auto p = mnMap . find ( proTxHash ) ;
if ( p = = nullptr ) {
return false ;
}
return IsMNValid ( * p ) ;
}
bool CDeterministicMNList : : IsMNPoSeBanned ( const uint256 & proTxHash ) const
{
auto p = mnMap . find ( proTxHash ) ;
if ( p = = nullptr ) {
return false ;
}
return IsMNPoSeBanned ( * p ) ;
}
bool CDeterministicMNList : : IsMNValid ( const CDeterministicMNCPtr & dmn ) const
{
return ! IsMNPoSeBanned ( dmn ) ;
}
bool CDeterministicMNList : : IsMNPoSeBanned ( const CDeterministicMNCPtr & dmn ) const
{
assert ( dmn ) ;
const CDeterministicMNState & state = * dmn - > pdmnState ;
return state . nPoSeBanHeight ! = - 1 ;
}
CDeterministicMNCPtr CDeterministicMNList : : GetMN ( const uint256 & proTxHash ) const
{
auto p = mnMap . find ( proTxHash ) ;
if ( p = = nullptr ) {
return nullptr ;
}
return * p ;
}
CDeterministicMNCPtr CDeterministicMNList : : GetValidMN ( const uint256 & proTxHash ) const
{
auto dmn = GetMN ( proTxHash ) ;
if ( dmn & & ! IsMNValid ( dmn ) ) {
return nullptr ;
}
return dmn ;
}
2018-10-21 21:45:16 +02:00
CDeterministicMNCPtr CDeterministicMNList : : GetMNByOperatorKey ( const CBLSPublicKey & pubKey )
2018-02-14 14:43:03 +01:00
{
for ( const auto & p : mnMap ) {
2018-10-21 21:45:16 +02:00
if ( p . second - > pdmnState - > pubKeyOperator = = pubKey ) {
2018-02-14 14:43:03 +01:00
return p . second ;
}
}
return nullptr ;
}
2018-10-25 16:29:50 +02:00
CDeterministicMNCPtr CDeterministicMNList : : GetMNByCollateral ( const COutPoint & collateralOutpoint ) const
{
return GetUniquePropertyMN ( collateralOutpoint ) ;
}
2018-11-06 09:54:23 +01:00
static int CompareByLastPaid_GetHeight ( const CDeterministicMN & dmn )
2018-02-14 14:43:03 +01:00
{
2018-11-06 09:54:23 +01:00
int height = dmn . pdmnState - > nLastPaidHeight ;
if ( dmn . pdmnState - > nPoSeRevivedHeight ! = - 1 & & dmn . pdmnState - > nPoSeRevivedHeight > height ) {
height = dmn . pdmnState - > nPoSeRevivedHeight ;
} else if ( height = = 0 ) {
height = dmn . pdmnState - > nRegisteredHeight ;
2018-02-14 14:43:03 +01:00
}
2018-11-06 09:54:23 +01:00
return height ;
2018-02-14 14:43:03 +01:00
}
2018-11-06 09:54:23 +01:00
static bool CompareByLastPaid ( const CDeterministicMN & _a , const CDeterministicMN & _b )
2018-02-14 14:43:03 +01:00
{
int ah = CompareByLastPaid_GetHeight ( _a ) ;
int bh = CompareByLastPaid_GetHeight ( _b ) ;
if ( ah = = bh ) {
return _a . proTxHash < _b . proTxHash ;
} else {
return ah < bh ;
}
}
2018-11-06 09:54:23 +01:00
static bool CompareByLastPaid ( const CDeterministicMNCPtr & _a , const CDeterministicMNCPtr & _b )
2018-02-14 14:43:03 +01:00
{
return CompareByLastPaid ( * _a , * _b ) ;
}
CDeterministicMNCPtr CDeterministicMNList : : GetMNPayee ( ) const
{
2018-10-26 07:03:14 +02:00
if ( mnMap . size ( ) = = 0 ) {
2018-02-14 14:43:03 +01:00
return nullptr ;
2018-10-26 07:03:14 +02:00
}
2018-02-14 14:43:03 +01:00
CDeterministicMNCPtr best ;
2018-10-02 11:03:05 +02:00
ForEachMN ( true , [ & ] ( const CDeterministicMNCPtr & dmn ) {
if ( ! best | | CompareByLastPaid ( dmn , best ) ) {
2018-02-14 14:43:03 +01:00
best = dmn ;
2018-10-02 11:03:05 +02:00
}
} ) ;
2018-02-14 14:43:03 +01:00
return best ;
}
std : : vector < CDeterministicMNCPtr > CDeterministicMNList : : GetProjectedMNPayees ( int nCount ) const
{
std : : vector < CDeterministicMNCPtr > result ;
result . reserve ( nCount ) ;
CDeterministicMNList tmpMNList = * this ;
for ( int h = nHeight ; h < nHeight + nCount ; h + + ) {
tmpMNList . SetHeight ( h ) ;
CDeterministicMNCPtr payee = tmpMNList . GetMNPayee ( ) ;
// push the original MN object instead of the one from the temporary list
result . push_back ( GetMN ( payee - > proTxHash ) ) ;
CDeterministicMNStatePtr newState = std : : make_shared < CDeterministicMNState > ( * payee - > pdmnState ) ;
newState - > nLastPaidHeight = h ;
tmpMNList . UpdateMN ( payee - > proTxHash , newState ) ;
}
return result ;
}
2018-11-13 13:24:14 +01:00
std : : vector < CDeterministicMNCPtr > CDeterministicMNList : : CalculateQuorum ( size_t maxSize , const uint256 & modifier ) const
{
auto scores = CalculateScores ( modifier ) ;
// sort is descending order
std : : sort ( scores . rbegin ( ) , scores . rend ( ) , [ ] ( const std : : pair < arith_uint256 , CDeterministicMNCPtr > & a , std : : pair < arith_uint256 , CDeterministicMNCPtr > & b ) {
if ( a . first = = b . first ) {
// this should actually never happen, but we should stay compatible with how the non deterministic MNs did the sorting
return a . second - > collateralOutpoint < b . second - > collateralOutpoint ;
}
return a . first < b . first ;
} ) ;
// take top maxSize entries and return it
std : : vector < CDeterministicMNCPtr > result ;
result . resize ( std : : min ( maxSize , scores . size ( ) ) ) ;
for ( size_t i = 0 ; i < result . size ( ) ; i + + ) {
result [ i ] = std : : move ( scores [ i ] . second ) ;
}
return result ;
}
std : : vector < std : : pair < arith_uint256 , CDeterministicMNCPtr > > CDeterministicMNList : : CalculateScores ( const uint256 & modifier ) const
{
std : : vector < std : : pair < arith_uint256 , CDeterministicMNCPtr > > scores ;
scores . reserve ( GetAllMNsCount ( ) ) ;
ForEachMN ( true , [ & ] ( const CDeterministicMNCPtr & dmn ) {
if ( dmn - > pdmnState - > confirmedHash . IsNull ( ) ) {
// we only take confirmed MNs into account to avoid hash grinding on the ProRegTxHash to sneak MNs into a
// future quorums
return ;
}
// calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN
// Please note that this is not a double-sha256 but a single-sha256
// The first part is already precalculated (confirmedHashWithProRegTxHash)
// TODO When https://github.com/bitcoin/bitcoin/pull/13191 gets backported, implement something that is similar but for single-sha256
uint256 h ;
CSHA256 sha256 ;
sha256 . Write ( dmn - > pdmnState - > confirmedHashWithProRegTxHash . begin ( ) , dmn - > pdmnState - > confirmedHashWithProRegTxHash . size ( ) ) ;
sha256 . Write ( modifier . begin ( ) , modifier . size ( ) ) ;
sha256 . Finalize ( h . begin ( ) ) ;
scores . emplace_back ( UintToArith256 ( h ) , dmn ) ;
} ) ;
return scores ;
}
2018-11-25 14:27:18 +01:00
int CDeterministicMNList : : CalcMaxPoSePenalty ( ) const
{
// Maximum PoSe penalty is dynamic and equals the number of registered MNs
// It's however at least 100.
// This means that the max penalty is usually equal to a full payment cycle
return std : : max ( 100 , ( int ) GetAllMNsCount ( ) ) ;
}
int CDeterministicMNList : : CalcPenalty ( int percent ) const
{
assert ( percent > 0 ) ;
return ( CalcMaxPoSePenalty ( ) * percent ) / 100 ;
}
2018-12-06 08:06:37 +01:00
void CDeterministicMNList : : PoSePunish ( const uint256 & proTxHash , int penalty , bool debugLogs )
2018-11-25 14:27:18 +01:00
{
assert ( penalty > 0 ) ;
auto dmn = GetMN ( proTxHash ) ;
assert ( dmn ) ;
int maxPenalty = CalcMaxPoSePenalty ( ) ;
auto newState = std : : make_shared < CDeterministicMNState > ( * dmn - > pdmnState ) ;
newState - > nPoSePenalty + = penalty ;
newState - > nPoSePenalty = std : : min ( maxPenalty , newState - > nPoSePenalty ) ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNList::%s -- punished MN %s, penalty %d->%d (max=%d) \n " ,
__func__ , proTxHash . ToString ( ) , dmn - > pdmnState - > nPoSePenalty , newState - > nPoSePenalty , maxPenalty ) ;
}
2018-11-25 14:27:18 +01:00
if ( newState - > nPoSePenalty > = maxPenalty & & newState - > nPoSeBanHeight = = - 1 ) {
newState - > nPoSeBanHeight = nHeight ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNList::%s -- banned MN %s at height %d \n " ,
__func__ , proTxHash . ToString ( ) , nHeight ) ;
}
2018-11-25 14:27:18 +01:00
}
UpdateMN ( proTxHash , newState ) ;
}
void CDeterministicMNList : : PoSeDecrease ( const uint256 & proTxHash )
{
auto dmn = GetMN ( proTxHash ) ;
assert ( dmn ) ;
assert ( dmn - > pdmnState - > nPoSePenalty > 0 & & dmn - > pdmnState - > nPoSeBanHeight = = - 1 ) ;
auto newState = std : : make_shared < CDeterministicMNState > ( * dmn - > pdmnState ) ;
newState - > nPoSePenalty - - ;
UpdateMN ( proTxHash , newState ) ;
}
2018-02-14 14:43:03 +01:00
CDeterministicMNListDiff CDeterministicMNList : : BuildDiff ( const CDeterministicMNList & to ) const
{
CDeterministicMNListDiff diffRet ;
diffRet . prevBlockHash = blockHash ;
diffRet . blockHash = to . blockHash ;
diffRet . nHeight = to . nHeight ;
2018-11-15 10:41:14 +01:00
to . ForEachMN ( false , [ & ] ( const CDeterministicMNCPtr & toPtr ) {
auto fromPtr = GetMN ( toPtr - > proTxHash ) ;
2018-02-14 14:43:03 +01:00
if ( fromPtr = = nullptr ) {
2018-11-15 10:41:14 +01:00
diffRet . addedMNs . emplace ( toPtr - > proTxHash , toPtr ) ;
} else if ( * toPtr - > pdmnState ! = * fromPtr - > pdmnState ) {
2018-11-15 15:24:24 +01:00
diffRet . updatedMNs . emplace ( toPtr - > proTxHash , toPtr - > pdmnState ) ;
2018-02-14 14:43:03 +01:00
}
2018-11-15 10:41:14 +01:00
} ) ;
ForEachMN ( false , [ & ] ( const CDeterministicMNCPtr & fromPtr ) {
auto toPtr = to . GetMN ( fromPtr - > proTxHash ) ;
2018-02-14 14:43:03 +01:00
if ( toPtr = = nullptr ) {
2018-11-15 10:41:14 +01:00
diffRet . removedMns . insert ( fromPtr - > proTxHash ) ;
2018-02-14 14:43:03 +01:00
}
2018-11-15 10:41:14 +01:00
} ) ;
2018-02-14 14:43:03 +01:00
return diffRet ;
}
2018-11-15 10:42:39 +01:00
CSimplifiedMNListDiff CDeterministicMNList : : BuildSimplifiedDiff ( const CDeterministicMNList & to ) const
{
CSimplifiedMNListDiff diffRet ;
diffRet . baseBlockHash = blockHash ;
diffRet . blockHash = to . blockHash ;
to . ForEachMN ( false , [ & ] ( const CDeterministicMNCPtr & toPtr ) {
2018-11-15 15:24:24 +01:00
auto fromPtr = GetMN ( toPtr - > proTxHash ) ;
2018-11-15 10:42:39 +01:00
if ( fromPtr = = nullptr ) {
diffRet . mnList . emplace_back ( * toPtr ) ;
} else {
CSimplifiedMNListEntry sme1 ( * toPtr ) ;
2018-11-15 15:24:24 +01:00
CSimplifiedMNListEntry sme2 ( * fromPtr ) ;
2018-11-15 10:42:39 +01:00
if ( sme1 ! = sme2 ) {
diffRet . mnList . emplace_back ( * toPtr ) ;
}
}
} ) ;
ForEachMN ( false , [ & ] ( const CDeterministicMNCPtr & fromPtr ) {
2018-11-15 15:24:24 +01:00
auto toPtr = to . GetMN ( fromPtr - > proTxHash ) ;
2018-11-15 10:42:39 +01:00
if ( toPtr = = nullptr ) {
diffRet . deletedMNs . emplace_back ( fromPtr - > proTxHash ) ;
}
} ) ;
return diffRet ;
}
2018-02-14 14:43:03 +01:00
CDeterministicMNList CDeterministicMNList : : ApplyDiff ( const CDeterministicMNListDiff & diff ) const
{
assert ( diff . prevBlockHash = = blockHash & & diff . nHeight = = nHeight + 1 ) ;
CDeterministicMNList result = * this ;
result . blockHash = diff . blockHash ;
result . nHeight = diff . nHeight ;
for ( const auto & hash : diff . removedMns ) {
result . RemoveMN ( hash ) ;
}
for ( const auto & p : diff . addedMNs ) {
result . AddMN ( p . second ) ;
}
for ( const auto & p : diff . updatedMNs ) {
result . UpdateMN ( p . first , p . second ) ;
}
return result ;
}
2018-11-06 09:54:23 +01:00
void CDeterministicMNList : : AddMN ( const CDeterministicMNCPtr & dmn )
2018-02-14 14:43:03 +01:00
{
assert ( ! mnMap . find ( dmn - > proTxHash ) ) ;
mnMap = mnMap . set ( dmn - > proTxHash , dmn ) ;
2018-10-25 16:29:50 +02:00
AddUniqueProperty ( dmn , dmn - > collateralOutpoint ) ;
2018-02-14 14:43:03 +01:00
AddUniqueProperty ( dmn , dmn - > pdmnState - > addr ) ;
AddUniqueProperty ( dmn , dmn - > pdmnState - > keyIDOwner ) ;
2018-10-21 21:45:16 +02:00
AddUniqueProperty ( dmn , dmn - > pdmnState - > pubKeyOperator ) ;
2018-02-14 14:43:03 +01:00
}
2018-11-06 09:54:23 +01:00
void CDeterministicMNList : : UpdateMN ( const uint256 & proTxHash , const CDeterministicMNStateCPtr & pdmnState )
2018-02-14 14:43:03 +01:00
{
auto oldDmn = mnMap . find ( proTxHash ) ;
assert ( oldDmn ! = nullptr ) ;
auto dmn = std : : make_shared < CDeterministicMN > ( * * oldDmn ) ;
auto oldState = dmn - > pdmnState ;
dmn - > pdmnState = pdmnState ;
mnMap = mnMap . set ( proTxHash , dmn ) ;
UpdateUniqueProperty ( dmn , oldState - > addr , pdmnState - > addr ) ;
UpdateUniqueProperty ( dmn , oldState - > keyIDOwner , pdmnState - > keyIDOwner ) ;
2018-10-21 21:45:16 +02:00
UpdateUniqueProperty ( dmn , oldState - > pubKeyOperator , pdmnState - > pubKeyOperator ) ;
2018-02-14 14:43:03 +01:00
}
void CDeterministicMNList : : RemoveMN ( const uint256 & proTxHash )
{
auto dmn = GetMN ( proTxHash ) ;
assert ( dmn ! = nullptr ) ;
2018-10-25 16:29:50 +02:00
DeleteUniqueProperty ( dmn , dmn - > collateralOutpoint ) ;
2018-02-14 14:43:03 +01:00
DeleteUniqueProperty ( dmn , dmn - > pdmnState - > addr ) ;
DeleteUniqueProperty ( dmn , dmn - > pdmnState - > keyIDOwner ) ;
2018-10-21 21:45:16 +02:00
DeleteUniqueProperty ( dmn , dmn - > pdmnState - > pubKeyOperator ) ;
2018-02-14 14:43:03 +01:00
mnMap = mnMap . erase ( proTxHash ) ;
}
CDeterministicMNManager : : CDeterministicMNManager ( CEvoDB & _evoDb ) :
evoDb ( _evoDb )
{
}
bool CDeterministicMNManager : : ProcessBlock ( const CBlock & block , const CBlockIndex * pindexPrev , CValidationState & _state )
{
LOCK ( cs ) ;
int nHeight = pindexPrev - > nHeight + 1 ;
CDeterministicMNList newList ;
2018-12-06 08:06:37 +01:00
if ( ! BuildNewListFromBlock ( block , pindexPrev , _state , newList , true ) ) {
2018-02-14 14:43:03 +01:00
return false ;
}
if ( newList . GetHeight ( ) = = - 1 ) {
newList . SetHeight ( nHeight ) ;
}
newList . SetBlockHash ( block . GetHash ( ) ) ;
CDeterministicMNList oldList = GetListForBlock ( pindexPrev - > GetBlockHash ( ) ) ;
CDeterministicMNListDiff diff = oldList . BuildDiff ( newList ) ;
evoDb . Write ( std : : make_pair ( DB_LIST_DIFF , diff . blockHash ) , diff ) ;
if ( ( nHeight % SNAPSHOT_LIST_PERIOD ) = = 0 ) {
evoDb . Write ( std : : make_pair ( DB_LIST_SNAPSHOT , diff . blockHash ) , newList ) ;
2018-10-02 11:03:05 +02:00
LogPrintf ( " CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.allMNsCount=%d \n " ,
2018-11-06 09:54:23 +01:00
__func__ , nHeight , newList . GetAllMNsCount ( ) ) ;
2018-02-14 14:43:03 +01:00
}
2018-02-15 13:57:18 +01:00
if ( nHeight = = GetSpork15Value ( ) ) {
LogPrintf ( " CDeterministicMNManager::%s -- spork15 is active now. nHeight=%d \n " , __func__ , nHeight ) ;
}
2018-02-14 14:43:03 +01:00
CleanupCache ( nHeight ) ;
return true ;
}
bool CDeterministicMNManager : : UndoBlock ( const CBlock & block , const CBlockIndex * pindex )
{
LOCK ( cs ) ;
int nHeight = pindex - > nHeight ;
2018-09-03 12:18:20 +02:00
uint256 blockHash = block . GetHash ( ) ;
2018-02-14 14:43:03 +01:00
2018-09-03 12:18:20 +02:00
evoDb . Erase ( std : : make_pair ( DB_LIST_DIFF , blockHash ) ) ;
evoDb . Erase ( std : : make_pair ( DB_LIST_SNAPSHOT , blockHash ) ) ;
mnListsCache . erase ( blockHash ) ;
2018-02-14 14:43:03 +01:00
2018-02-15 13:57:18 +01:00
if ( nHeight = = GetSpork15Value ( ) ) {
LogPrintf ( " CDeterministicMNManager::%s -- spork15 is not active anymore. nHeight=%d \n " , __func__ , nHeight ) ;
}
2018-02-14 14:43:03 +01:00
return true ;
}
void CDeterministicMNManager : : UpdatedBlockTip ( const CBlockIndex * pindex )
{
LOCK ( cs ) ;
tipHeight = pindex - > nHeight ;
tipBlockHash = pindex - > GetBlockHash ( ) ;
}
2018-12-06 08:06:37 +01:00
bool CDeterministicMNManager : : BuildNewListFromBlock ( const CBlock & block , const CBlockIndex * pindexPrev , CValidationState & _state , CDeterministicMNList & mnListRet , bool debugLogs )
2018-02-14 14:43:03 +01:00
{
AssertLockHeld ( cs ) ;
int nHeight = pindexPrev - > nHeight + 1 ;
CDeterministicMNList oldList = GetListForBlock ( pindexPrev - > GetBlockHash ( ) ) ;
CDeterministicMNList newList = oldList ;
newList . SetBlockHash ( uint256 ( ) ) ; // we can't know the final block hash, so better not return a (invalid) block hash
newList . SetHeight ( nHeight ) ;
auto payee = oldList . GetMNPayee ( ) ;
2018-11-13 13:24:14 +01:00
// we iterate the oldList here and update the newList
// this is only valid as long these have not diverged at this point, which is the case as long as we don't add
// code above this loop that modifies newList
oldList . ForEachMN ( false , [ & ] ( const CDeterministicMNCPtr & dmn ) {
if ( ! dmn - > pdmnState - > confirmedHash . IsNull ( ) ) {
// already confirmed
return ;
}
// this works on the previous block, so confirmation will happen one block after nMasternodeMinimumConfirmations
// has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations
int nConfirmations = pindexPrev - > nHeight - dmn - > pdmnState - > nRegisteredHeight ;
if ( nConfirmations > = Params ( ) . GetConsensus ( ) . nMasternodeMinimumConfirmations ) {
CDeterministicMNState newState = * dmn - > pdmnState ;
newState . UpdateConfirmedHash ( dmn - > proTxHash , pindexPrev - > GetBlockHash ( ) ) ;
newList . UpdateMN ( dmn - > proTxHash , std : : make_shared < CDeterministicMNState > ( newState ) ) ;
}
} ) ;
2018-11-25 14:27:18 +01:00
DecreasePoSePenalties ( newList ) ;
2018-02-14 14:43:03 +01:00
// we skip the coinbase
for ( int i = 1 ; i < ( int ) block . vtx . size ( ) ; i + + ) {
const CTransaction & tx = * block . vtx [ i ] ;
// check if any existing MN collateral is spent by this transaction
for ( const auto & in : tx . vin ) {
2018-10-25 16:29:50 +02:00
auto dmn = newList . GetMNByCollateral ( in . prevout ) ;
if ( dmn & & dmn - > collateralOutpoint = = in . prevout ) {
newList . RemoveMN ( dmn - > proTxHash ) ;
2018-02-14 14:43:03 +01:00
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d \n " ,
__func__ , dmn - > proTxHash . ToString ( ) , dmn - > collateralOutpoint . ToStringShort ( ) , nHeight , newList . GetAllMNsCount ( ) ) ;
}
2018-02-14 14:43:03 +01:00
}
}
2018-11-25 14:27:18 +01:00
if ( tx . nVersion ! = 3 ) {
// only interested in special TXs
continue ;
}
2018-02-14 14:43:03 +01:00
if ( tx . nType = = TRANSACTION_PROVIDER_REGISTER ) {
CProRegTx proTx ;
if ( ! GetTxPayload ( tx , proTx ) ) {
assert ( false ) ; // this should have been handled already
}
2018-10-25 16:29:50 +02:00
auto dmn = std : : make_shared < CDeterministicMN > ( ) ;
dmn - > proTxHash = tx . GetHash ( ) ;
// collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself
if ( proTx . collateralOutpoint . hash . IsNull ( ) ) {
dmn - > collateralOutpoint = COutPoint ( tx . GetHash ( ) , proTx . collateralOutpoint . n ) ;
} else {
dmn - > collateralOutpoint = proTx . collateralOutpoint ;
}
Coin coin ;
if ( ! proTx . collateralOutpoint . hash . IsNull ( ) & & ( ! GetUTXOCoin ( dmn - > collateralOutpoint , coin ) | | coin . out . nValue ! = 1000 * COIN ) ) {
// should actually never get to this point as CheckProRegTx should have handled this case.
// We do this additional check nevertheless to be 100% sure
return _state . DoS ( 100 , false , REJECT_INVALID , " bad-protx-collateral " ) ;
}
2018-11-10 10:54:16 +01:00
auto replacedDmn = newList . GetMNByCollateral ( dmn - > collateralOutpoint ) ;
if ( replacedDmn ! = nullptr ) {
// This might only happen with a ProRegTx that refers an external collateral
// In that case the new ProRegTx will replace the old one. This means the old one is removed
// and the new one is added like a completely fresh one, which is also at the bottom of the payment list
newList . RemoveMN ( replacedDmn - > proTxHash ) ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNManager::%s -- MN %s removed from list because collateral was used for a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d \n " ,
__func__ , replacedDmn - > proTxHash . ToString ( ) , dmn - > collateralOutpoint . ToStringShort ( ) , nHeight , newList . GetAllMNsCount ( ) ) ;
}
2018-11-10 10:54:16 +01:00
}
if ( newList . HasUniqueProperty ( proTx . addr ) ) {
return _state . DoS ( 100 , false , REJECT_CONFLICT , " bad-protx-dup-addr " ) ;
}
if ( newList . HasUniqueProperty ( proTx . keyIDOwner ) | | newList . HasUniqueProperty ( proTx . pubKeyOperator ) ) {
return _state . DoS ( 100 , false , REJECT_CONFLICT , " bad-protx-dup-key " ) ;
}
2018-10-25 16:29:50 +02:00
dmn - > nOperatorReward = proTx . nOperatorReward ;
dmn - > pdmnState = std : : make_shared < CDeterministicMNState > ( proTx ) ;
2018-02-14 14:43:03 +01:00
CDeterministicMNState dmnState = * dmn - > pdmnState ;
dmnState . nRegisteredHeight = nHeight ;
2018-10-23 13:15:38 +02:00
if ( proTx . addr = = CService ( ) ) {
2018-02-14 14:43:03 +01:00
// start in banned pdmnState as we need to wait for a ProUpServTx
dmnState . nPoSeBanHeight = nHeight ;
}
dmn - > pdmnState = std : : make_shared < CDeterministicMNState > ( dmnState ) ;
newList . AddMN ( dmn ) ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNManager::%s -- MN %s added at height %d: %s \n " ,
__func__ , tx . GetHash ( ) . ToString ( ) , nHeight , proTx . ToString ( ) ) ;
}
2018-03-13 19:10:58 +01:00
} else if ( tx . nType = = TRANSACTION_PROVIDER_UPDATE_SERVICE ) {
CProUpServTx proTx ;
if ( ! GetTxPayload ( tx , proTx ) ) {
assert ( false ) ; // this should have been handled already
}
2018-10-26 07:03:14 +02:00
if ( newList . HasUniqueProperty ( proTx . addr ) & & newList . GetUniquePropertyMN ( proTx . addr ) - > proTxHash ! = proTx . proTxHash ) {
2018-03-13 19:10:58 +01:00
return _state . DoS ( 100 , false , REJECT_CONFLICT , " bad-protx-dup-addr " ) ;
2018-10-26 07:03:14 +02:00
}
2018-03-13 19:10:58 +01:00
CDeterministicMNCPtr dmn = newList . GetMN ( proTx . proTxHash ) ;
if ( ! dmn ) {
return _state . DoS ( 100 , false , REJECT_INVALID , " bad-protx-hash " ) ;
}
auto newState = std : : make_shared < CDeterministicMNState > ( * dmn - > pdmnState ) ;
newState - > addr = proTx . addr ;
newState - > scriptOperatorPayout = proTx . scriptOperatorPayout ;
if ( newState - > nPoSeBanHeight ! = - 1 ) {
// only revive when all keys are set
2018-10-21 21:45:16 +02:00
if ( newState - > pubKeyOperator . IsValid ( ) & & ! newState - > keyIDVoting . IsNull ( ) & & ! newState - > keyIDOwner . IsNull ( ) ) {
2018-11-25 14:27:18 +01:00
newState - > nPoSePenalty = 0 ;
2018-03-13 19:10:58 +01:00
newState - > nPoSeBanHeight = - 1 ;
newState - > nPoSeRevivedHeight = nHeight ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNManager::%s -- MN %s revived at height %d \n " ,
__func__ , proTx . proTxHash . ToString ( ) , nHeight ) ;
}
2018-03-13 19:10:58 +01:00
}
}
newList . UpdateMN ( proTx . proTxHash , newState ) ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNManager::%s -- MN %s updated at height %d: %s \n " ,
__func__ , proTx . proTxHash . ToString ( ) , nHeight , proTx . ToString ( ) ) ;
}
2018-03-19 08:44:00 +01:00
} else if ( tx . nType = = TRANSACTION_PROVIDER_UPDATE_REGISTRAR ) {
CProUpRegTx proTx ;
if ( ! GetTxPayload ( tx , proTx ) ) {
assert ( false ) ; // this should have been handled already
}
CDeterministicMNCPtr dmn = newList . GetMN ( proTx . proTxHash ) ;
if ( ! dmn ) {
return _state . DoS ( 100 , false , REJECT_INVALID , " bad-protx-hash " ) ;
}
auto newState = std : : make_shared < CDeterministicMNState > ( * dmn - > pdmnState ) ;
2018-10-21 21:45:16 +02:00
if ( newState - > pubKeyOperator ! = proTx . pubKeyOperator ) {
2018-03-19 08:44:00 +01:00
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
newState - > ResetOperatorFields ( ) ;
newState - > BanIfNotBanned ( nHeight ) ;
}
2018-10-21 21:45:16 +02:00
newState - > pubKeyOperator = proTx . pubKeyOperator ;
2018-03-19 08:44:00 +01:00
newState - > keyIDVoting = proTx . keyIDVoting ;
newState - > scriptPayout = proTx . scriptPayout ;
newList . UpdateMN ( proTx . proTxHash , newState ) ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNManager::%s -- MN %s updated at height %d: %s \n " ,
__func__ , proTx . proTxHash . ToString ( ) , nHeight , proTx . ToString ( ) ) ;
}
2018-03-19 12:29:59 +01:00
} else if ( tx . nType = = TRANSACTION_PROVIDER_UPDATE_REVOKE ) {
CProUpRevTx proTx ;
if ( ! GetTxPayload ( tx , proTx ) ) {
assert ( false ) ; // this should have been handled already
}
CDeterministicMNCPtr dmn = newList . GetMN ( proTx . proTxHash ) ;
if ( ! dmn ) {
return _state . DoS ( 100 , false , REJECT_INVALID , " bad-protx-hash " ) ;
}
auto newState = std : : make_shared < CDeterministicMNState > ( * dmn - > pdmnState ) ;
newState - > ResetOperatorFields ( ) ;
newState - > BanIfNotBanned ( nHeight ) ;
newState - > nRevocationReason = proTx . nReason ;
newList . UpdateMN ( proTx . proTxHash , newState ) ;
2018-12-06 08:06:37 +01:00
if ( debugLogs ) {
LogPrintf ( " CDeterministicMNManager::%s -- MN %s revoked operator key at height %d: %s \n " ,
__func__ , proTx . proTxHash . ToString ( ) , nHeight , proTx . ToString ( ) ) ;
}
2018-11-25 14:27:18 +01:00
} else if ( tx . nType = = TRANSACTION_QUORUM_COMMITMENT ) {
2018-11-27 08:04:08 +01:00
llmq : : CFinalCommitmentTxPayload qc ;
2018-11-25 14:27:18 +01:00
if ( ! GetTxPayload ( tx , qc ) ) {
assert ( false ) ; // this should have been handled already
}
2018-11-27 08:04:08 +01:00
if ( ! qc . commitment . IsNull ( ) ) {
2018-12-06 08:06:37 +01:00
HandleQuorumCommitment ( qc . commitment , newList , debugLogs ) ;
2018-11-25 14:27:18 +01:00
}
2018-02-14 14:43:03 +01:00
}
}
// The payee for the current block was determined by the previous block's list but it might have disappeared in the
// current block. We still pay that MN one last time however.
if ( payee & & newList . HasMN ( payee - > proTxHash ) ) {
auto newState = std : : make_shared < CDeterministicMNState > ( * newList . GetMN ( payee - > proTxHash ) - > pdmnState ) ;
newState - > nLastPaidHeight = nHeight ;
newList . UpdateMN ( payee - > proTxHash , newState ) ;
}
mnListRet = std : : move ( newList ) ;
return true ;
}
2018-12-06 08:06:37 +01:00
void CDeterministicMNManager : : HandleQuorumCommitment ( llmq : : CFinalCommitment & qc , CDeterministicMNList & mnList , bool debugLogs )
2018-11-25 14:27:18 +01:00
{
// The commitment has already been validated at this point so it's safe to use members of it
auto members = llmq : : CLLMQUtils : : GetAllQuorumMembers ( ( Consensus : : LLMQType ) qc . llmqType , qc . quorumHash ) ;
for ( size_t i = 0 ; i < members . size ( ) ; i + + ) {
if ( ! mnList . HasMN ( members [ i ] - > proTxHash ) ) {
continue ;
}
if ( ! qc . validMembers [ i ] ) {
// punish MN for failed DKG participation
// The idea is to immediately ban a MN when it fails 2 DKG sessions with only a few blocks in-between
// If there were enough blocks between failures, the MN has a chance to recover as he reduces his penalty by 1 for every block
// If it however fails 3 times in the timespan of a single payment cycle, it should definitely get banned
2018-12-06 08:06:37 +01:00
mnList . PoSePunish ( members [ i ] - > proTxHash , mnList . CalcPenalty ( 66 ) , debugLogs ) ;
2018-11-25 14:27:18 +01:00
}
}
}
void CDeterministicMNManager : : DecreasePoSePenalties ( CDeterministicMNList & mnList )
{
std : : vector < uint256 > toDecrease ;
toDecrease . reserve ( mnList . GetValidMNsCount ( ) / 10 ) ;
// only iterate and decrease for valid ones (not PoSe banned yet)
// if a MN ever reaches the maximum, it stays in PoSe banned state until revived
mnList . ForEachMN ( true , [ & ] ( const CDeterministicMNCPtr & dmn ) {
if ( dmn - > pdmnState - > nPoSePenalty > 0 & & dmn - > pdmnState - > nPoSeBanHeight = = - 1 ) {
toDecrease . emplace_back ( dmn - > proTxHash ) ;
}
} ) ;
for ( const auto & proTxHash : toDecrease ) {
mnList . PoSeDecrease ( proTxHash ) ;
}
}
2018-02-14 14:43:03 +01:00
CDeterministicMNList CDeterministicMNManager : : GetListForBlock ( const uint256 & blockHash )
{
LOCK ( cs ) ;
auto it = mnListsCache . find ( blockHash ) ;
if ( it ! = mnListsCache . end ( ) ) {
return it - > second ;
}
uint256 blockHashTmp = blockHash ;
CDeterministicMNList snapshot ;
2018-09-03 12:17:41 +02:00
std : : list < CDeterministicMNListDiff > listDiff ;
2018-02-14 14:43:03 +01:00
2018-11-06 09:54:23 +01:00
while ( true ) {
2018-09-03 12:17:41 +02:00
// try using cache before reading from disk
it = mnListsCache . find ( blockHashTmp ) ;
if ( it ! = mnListsCache . end ( ) ) {
snapshot = it - > second ;
break ;
}
2018-02-14 14:43:03 +01:00
if ( evoDb . Read ( std : : make_pair ( DB_LIST_SNAPSHOT , blockHashTmp ) , snapshot ) ) {
break ;
}
CDeterministicMNListDiff diff ;
if ( ! evoDb . Read ( std : : make_pair ( DB_LIST_DIFF , blockHashTmp ) , diff ) ) {
snapshot = CDeterministicMNList ( blockHashTmp , - 1 ) ;
break ;
}
2018-09-03 12:17:41 +02:00
listDiff . emplace_front ( diff ) ;
2018-02-14 14:43:03 +01:00
blockHashTmp = diff . prevBlockHash ;
}
2018-09-03 12:17:41 +02:00
for ( const auto & diff : listDiff ) {
2018-02-14 14:43:03 +01:00
if ( diff . HasChanges ( ) ) {
snapshot = snapshot . ApplyDiff ( diff ) ;
} else {
snapshot . SetBlockHash ( diff . blockHash ) ;
snapshot . SetHeight ( diff . nHeight ) ;
}
}
mnListsCache . emplace ( blockHash , snapshot ) ;
return snapshot ;
}
CDeterministicMNList CDeterministicMNManager : : GetListAtChainTip ( )
{
LOCK ( cs ) ;
return GetListForBlock ( tipBlockHash ) ;
}
2018-10-25 16:29:50 +02:00
bool CDeterministicMNManager : : HasValidMNCollateralAtChainTip ( const COutPoint & outpoint )
2018-02-14 14:43:03 +01:00
{
2018-10-25 16:29:50 +02:00
auto mnList = GetListAtChainTip ( ) ;
auto dmn = mnList . GetMNByCollateral ( outpoint ) ;
return dmn & & mnList . IsMNValid ( dmn ) ;
2018-02-14 14:43:03 +01:00
}
2018-10-25 16:29:50 +02:00
bool CDeterministicMNManager : : HasMNCollateralAtChainTip ( const COutPoint & outpoint )
2018-02-14 14:43:03 +01:00
{
2018-10-25 16:29:50 +02:00
auto mnList = GetListAtChainTip ( ) ;
auto dmn = mnList . GetMNByCollateral ( outpoint ) ;
return dmn ! = nullptr ;
2018-02-14 14:43:03 +01:00
}
2018-10-25 16:29:50 +02:00
int64_t CDeterministicMNManager : : GetSpork15Value ( )
2018-02-14 14:43:03 +01:00
{
2018-10-25 16:29:50 +02:00
return sporkManager . GetSporkValue ( SPORK_15_DETERMINISTIC_MNS_ENABLED ) ;
2018-02-14 14:43:03 +01:00
}
2018-10-25 16:29:50 +02:00
bool CDeterministicMNManager : : IsProTxWithCollateral ( const CTransactionRef & tx , uint32_t n )
2018-02-15 13:57:18 +01:00
{
2018-11-14 09:51:02 +01:00
if ( tx - > nVersion ! = 3 | | tx - > nType ! = TRANSACTION_PROVIDER_REGISTER ) {
2018-10-25 16:29:50 +02:00
return false ;
}
CProRegTx proTx ;
if ( ! GetTxPayload ( * tx , proTx ) ) {
return false ;
}
if ( ! proTx . collateralOutpoint . hash . IsNull ( ) ) {
return false ;
}
if ( proTx . collateralOutpoint . n > = tx - > vout . size ( ) | | proTx . collateralOutpoint . n ! = n ) {
return false ;
}
if ( tx - > vout [ n ] . nValue ! = 1000 * COIN ) {
return false ;
}
return true ;
2018-02-15 13:57:18 +01:00
}
bool CDeterministicMNManager : : IsDeterministicMNsSporkActive ( int nHeight )
{
LOCK ( cs ) ;
if ( nHeight = = - 1 ) {
nHeight = tipHeight ;
}
int64_t spork15Value = GetSpork15Value ( ) ;
return nHeight > = spork15Value ;
}
2018-02-14 14:43:03 +01:00
void CDeterministicMNManager : : CleanupCache ( int nHeight )
{
AssertLockHeld ( cs ) ;
std : : vector < uint256 > toDelete ;
for ( const auto & p : mnListsCache ) {
if ( p . second . GetHeight ( ) + LISTS_CACHE_SIZE < nHeight ) {
toDelete . emplace_back ( p . first ) ;
}
}
for ( const auto & h : toDelete ) {
mnListsCache . erase ( h ) ;
}
}