2019-01-29 15:53:14 +01:00
// Copyright (c) 2018-2019 The Dash Core developers
2018-11-23 15:42:09 +01:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2020-03-19 23:46:56 +01:00
# include <llmq/quorums.h>
# include <llmq/quorums_utils.h>
2018-11-23 15:42:09 +01:00
2020-03-19 23:46:56 +01:00
# include <chainparams.h>
# include <random.h>
2020-03-16 11:09:54 +01:00
# include <spork.h>
2020-03-19 23:46:56 +01:00
# include <validation.h>
2018-11-23 15:42:09 +01:00
2020-03-17 10:04:31 +01:00
# include <masternode/masternode-meta.h>
2018-11-23 15:42:09 +01:00
namespace llmq
{
2020-12-29 11:30:50 +01:00
CCriticalSection cs_llmq_vbc ;
VersionBitsCache llmq_versionbitscache ;
2019-07-09 07:59:57 +02:00
std : : vector < CDeterministicMNCPtr > CLLMQUtils : : GetAllQuorumMembers ( Consensus : : LLMQType llmqType , const CBlockIndex * pindexQuorum )
2018-11-23 15:42:09 +01:00
{
2020-12-10 00:08:05 +01:00
if ( ! IsQuorumTypeEnabled ( llmqType , pindexQuorum - > pprev ) ) {
return { } ;
}
2018-11-23 15:42:09 +01:00
auto & params = Params ( ) . GetConsensus ( ) . llmqs . at ( llmqType ) ;
2019-07-09 07:59:57 +02:00
auto allMns = deterministicMNManager - > GetListForBlock ( pindexQuorum ) ;
auto modifier = : : SerializeHash ( std : : make_pair ( llmqType , pindexQuorum - > GetBlockHash ( ) ) ) ;
2018-11-23 15:42:09 +01:00
return allMns . CalculateQuorum ( params . size , modifier ) ;
}
2019-05-28 15:34:41 +02:00
uint256 CLLMQUtils : : BuildCommitmentHash ( Consensus : : LLMQType llmqType , const uint256 & blockHash , const std : : vector < bool > & validMembers , const CBLSPublicKey & pubKey , const uint256 & vvecHash )
2018-11-23 15:42:09 +01:00
{
CHashWriter hw ( SER_NETWORK , 0 ) ;
hw < < llmqType ;
hw < < blockHash ;
hw < < DYNBITSET ( validMembers ) ;
hw < < pubKey ;
hw < < vvecHash ;
return hw . GetHash ( ) ;
}
2019-01-15 15:35:26 +01:00
uint256 CLLMQUtils : : BuildSignHash ( Consensus : : LLMQType llmqType , const uint256 & quorumHash , const uint256 & id , const uint256 & msgHash )
{
CHashWriter h ( SER_GETHASH , 0 ) ;
2019-05-28 15:34:41 +02:00
h < < llmqType ;
2019-01-15 15:35:26 +01:00
h < < quorumHash ;
h < < id ;
h < < msgHash ;
return h . GetHash ( ) ;
}
2021-01-11 04:23:01 +01:00
static bool EvalSpork ( Consensus : : LLMQType llmqType , int64_t spork_value )
2020-06-25 09:47:24 +02:00
{
2021-01-11 04:23:01 +01:00
if ( spork_value = = 0 ) {
2020-06-25 09:47:24 +02:00
return true ;
}
2021-01-11 04:23:01 +01:00
if ( spork_value = = 1 & & llmqType ! = Consensus : : LLMQ_100_67 & & llmqType ! = Consensus : : LLMQ_400_60 & & llmqType ! = Consensus : : LLMQ_400_85 ) {
2020-06-25 09:47:24 +02:00
return true ;
}
return false ;
}
2021-01-11 04:23:01 +01:00
bool CLLMQUtils : : IsAllMembersConnectedEnabled ( Consensus : : LLMQType llmqType )
{
return EvalSpork ( llmqType , sporkManager . GetSporkValue ( SPORK_21_QUORUM_ALL_CONNECTED ) ) ;
}
bool CLLMQUtils : : IsQuorumPoseEnabled ( Consensus : : LLMQType llmqType )
{
return EvalSpork ( llmqType , sporkManager . GetSporkValue ( SPORK_23_QUORUM_POSE ) ) ;
}
2020-04-16 13:57:32 +02:00
uint256 CLLMQUtils : : DeterministicOutboundConnection ( const uint256 & proTxHash1 , const uint256 & proTxHash2 )
{
// We need to deterministically select who is going to initiate the connection. The naive way would be to simply
// return the min(proTxHash1, proTxHash2), but this would create a bias towards MNs with a numerically low
// hash. To fix this, we return the proTxHash that has the lowest value of:
// hash(min(proTxHash1, proTxHash2), max(proTxHash1, proTxHash2), proTxHashX)
// where proTxHashX is the proTxHash to compare
uint256 h1 ;
uint256 h2 ;
if ( proTxHash1 < proTxHash2 ) {
h1 = : : SerializeHash ( std : : make_tuple ( proTxHash1 , proTxHash2 , proTxHash1 ) ) ;
h2 = : : SerializeHash ( std : : make_tuple ( proTxHash1 , proTxHash2 , proTxHash2 ) ) ;
} else {
h1 = : : SerializeHash ( std : : make_tuple ( proTxHash2 , proTxHash1 , proTxHash1 ) ) ;
h2 = : : SerializeHash ( std : : make_tuple ( proTxHash2 , proTxHash1 , proTxHash2 ) ) ;
}
if ( h1 < h2 ) {
return proTxHash1 ;
}
return proTxHash2 ;
}
2020-03-26 16:16:35 +01:00
std : : set < uint256 > CLLMQUtils : : GetQuorumConnections ( Consensus : : LLMQType llmqType , const CBlockIndex * pindexQuorum , const uint256 & forMember , bool onlyOutbound )
2018-05-24 16:14:55 +02:00
{
2020-06-25 09:47:24 +02:00
if ( IsAllMembersConnectedEnabled ( llmqType ) ) {
2020-06-18 10:40:33 +02:00
auto mns = GetAllQuorumMembers ( llmqType , pindexQuorum ) ;
std : : set < uint256 > result ;
2020-03-16 11:09:54 +01:00
for ( auto & dmn : mns ) {
2020-04-21 16:43:59 +02:00
if ( dmn - > proTxHash = = forMember ) {
continue ;
}
2020-04-16 13:57:32 +02:00
// Determine which of the two MNs (forMember vs dmn) should initiate the outbound connection and which
// one should wait for the inbound connection. We do this in a deterministic way, so that even when we
// end up with both connecting to each other, we know which one to disconnect
uint256 deterministicOutbound = DeterministicOutboundConnection ( forMember , dmn - > proTxHash ) ;
if ( ! onlyOutbound | | deterministicOutbound = = dmn - > proTxHash ) {
2020-03-16 11:09:54 +01:00
result . emplace ( dmn - > proTxHash ) ;
}
}
return result ;
2020-06-18 10:40:33 +02:00
} else {
return GetQuorumRelayMembers ( llmqType , pindexQuorum , forMember , onlyOutbound ) ;
2020-03-16 11:09:54 +01:00
}
2020-06-18 10:40:33 +02:00
}
2020-03-16 11:09:54 +01:00
2020-06-18 10:40:33 +02:00
std : : set < uint256 > CLLMQUtils : : GetQuorumRelayMembers ( Consensus : : LLMQType llmqType , const CBlockIndex * pindexQuorum , const uint256 & forMember , bool onlyOutbound )
{
auto mns = GetAllQuorumMembers ( llmqType , pindexQuorum ) ;
std : : set < uint256 > result ;
2020-03-16 11:09:54 +01:00
2020-03-27 15:09:15 +01:00
auto calcOutbound = [ & ] ( size_t i , const uint256 proTxHash ) {
2020-06-18 10:40:33 +02:00
// Relay to nodes at indexes (i+2^k)%n, where
2020-03-27 15:09:15 +01:00
// k: 0..max(1, floor(log2(n-1))-1)
// n: size of the quorum/ring
std : : set < uint256 > r ;
int gap = 1 ;
int gap_max = ( int ) mns . size ( ) - 1 ;
int k = 0 ;
while ( ( gap_max > > = 1 ) | | k < = 1 ) {
size_t idx = ( i + gap ) % mns . size ( ) ;
auto & otherDmn = mns [ idx ] ;
if ( otherDmn - > proTxHash = = proTxHash ) {
continue ;
}
r . emplace ( otherDmn - > proTxHash ) ;
gap < < = 1 ;
k + + ;
}
return r ;
} ;
2018-05-24 16:14:55 +02:00
for ( size_t i = 0 ; i < mns . size ( ) ; i + + ) {
auto & dmn = mns [ i ] ;
if ( dmn - > proTxHash = = forMember ) {
2020-03-27 15:09:15 +01:00
auto r = calcOutbound ( i , dmn - > proTxHash ) ;
result . insert ( r . begin ( ) , r . end ( ) ) ;
2020-03-27 15:11:13 +01:00
} else if ( ! onlyOutbound ) {
auto r = calcOutbound ( i , dmn - > proTxHash ) ;
if ( r . count ( forMember ) ) {
result . emplace ( dmn - > proTxHash ) ;
}
2018-05-24 16:14:55 +02:00
}
}
2020-03-27 15:11:13 +01:00
2018-05-24 16:14:55 +02:00
return result ;
}
2019-07-09 07:59:57 +02:00
std : : set < size_t > CLLMQUtils : : CalcDeterministicWatchConnections ( Consensus : : LLMQType llmqType , const CBlockIndex * pindexQuorum , size_t memberCount , size_t connectionCount )
2018-05-24 16:14:55 +02:00
{
static uint256 qwatchConnectionSeed ;
static std : : atomic < bool > qwatchConnectionSeedGenerated { false } ;
static CCriticalSection qwatchConnectionSeedCs ;
if ( ! qwatchConnectionSeedGenerated ) {
LOCK ( qwatchConnectionSeedCs ) ;
if ( ! qwatchConnectionSeedGenerated ) {
qwatchConnectionSeed = GetRandHash ( ) ;
qwatchConnectionSeedGenerated = true ;
}
}
std : : set < size_t > result ;
uint256 rnd = qwatchConnectionSeed ;
for ( size_t i = 0 ; i < connectionCount ; i + + ) {
2019-07-09 07:59:57 +02:00
rnd = : : SerializeHash ( std : : make_pair ( rnd , std : : make_pair ( llmqType , pindexQuorum - > GetBlockHash ( ) ) ) ) ;
2018-05-24 16:14:55 +02:00
result . emplace ( rnd . GetUint64 ( 0 ) % memberCount ) ;
}
return result ;
}
2018-11-23 15:42:09 +01:00
2020-03-16 10:54:59 +01:00
void CLLMQUtils : : EnsureQuorumConnections ( Consensus : : LLMQType llmqType , const CBlockIndex * pindexQuorum , const uint256 & myProTxHash , bool allowWatch )
{
auto members = GetAllQuorumMembers ( llmqType , pindexQuorum ) ;
bool isMember = std : : find_if ( members . begin ( ) , members . end ( ) , [ & ] ( const CDeterministicMNCPtr & dmn ) { return dmn - > proTxHash = = myProTxHash ; } ) ! = members . end ( ) ;
if ( ! isMember & & ! allowWatch ) {
return ;
}
std : : set < uint256 > connections ;
if ( isMember ) {
2020-03-26 16:16:35 +01:00
connections = CLLMQUtils : : GetQuorumConnections ( llmqType , pindexQuorum , myProTxHash , true ) ;
2020-03-16 10:54:59 +01:00
} else {
auto cindexes = CLLMQUtils : : CalcDeterministicWatchConnections ( llmqType , pindexQuorum , members . size ( ) , 1 ) ;
for ( auto idx : cindexes ) {
connections . emplace ( members [ idx ] - > proTxHash ) ;
}
}
if ( ! connections . empty ( ) ) {
2020-03-19 11:35:44 +01:00
if ( ! g_connman - > HasMasternodeQuorumNodes ( llmqType , pindexQuorum - > GetBlockHash ( ) ) & & LogAcceptCategory ( BCLog : : LLMQ ) ) {
2020-03-16 10:54:59 +01:00
auto mnList = deterministicMNManager - > GetListAtChainTip ( ) ;
std : : string debugMsg = strprintf ( " CLLMQUtils::%s -- adding masternodes quorum connections for quorum %s: \n " , __func__ , pindexQuorum - > GetBlockHash ( ) . ToString ( ) ) ;
for ( auto & c : connections ) {
auto dmn = mnList . GetValidMN ( c ) ;
if ( ! dmn ) {
debugMsg + = strprintf ( " %s (not in valid MN set anymore) \n " , c . ToString ( ) ) ;
} else {
debugMsg + = strprintf ( " %s (%s) \n " , c . ToString ( ) , dmn - > pdmnState - > addr . ToString ( false ) ) ;
}
}
2020-07-29 13:50:36 +02:00
LogPrint ( BCLog : : NET_NETCONN , debugMsg . c_str ( ) ) ; /* Continued */
2020-03-16 10:54:59 +01:00
}
2020-03-16 11:06:29 +01:00
g_connman - > SetMasternodeQuorumNodes ( llmqType , pindexQuorum - > GetBlockHash ( ) , connections ) ;
2020-03-16 10:54:59 +01:00
}
}
2020-03-17 10:04:31 +01:00
void CLLMQUtils : : AddQuorumProbeConnections ( Consensus : : LLMQType llmqType , const CBlockIndex * pindexQuorum , const uint256 & myProTxHash )
{
2021-01-11 04:23:01 +01:00
if ( ! CLLMQUtils : : IsQuorumPoseEnabled ( llmqType ) ) {
return ;
}
2020-03-17 10:04:31 +01:00
auto members = GetAllQuorumMembers ( llmqType , pindexQuorum ) ;
auto curTime = GetAdjustedTime ( ) ;
std : : set < uint256 > probeConnections ;
for ( auto & dmn : members ) {
if ( dmn - > proTxHash = = myProTxHash ) {
continue ;
}
auto lastOutbound = mmetaman . GetMetaInfo ( dmn - > proTxHash ) - > GetLastOutboundSuccess ( ) ;
// re-probe after 50 minutes so that the "good connection" check in the DKG doesn't fail just because we're on
// the brink of timeout
if ( curTime - lastOutbound > 50 * 60 ) {
probeConnections . emplace ( dmn - > proTxHash ) ;
}
}
if ( ! probeConnections . empty ( ) ) {
if ( LogAcceptCategory ( BCLog : : LLMQ ) ) {
auto mnList = deterministicMNManager - > GetListAtChainTip ( ) ;
std : : string debugMsg = strprintf ( " CLLMQUtils::%s -- adding masternodes probes for quorum %s: \n " , __func__ , pindexQuorum - > GetBlockHash ( ) . ToString ( ) ) ;
for ( auto & c : probeConnections ) {
auto dmn = mnList . GetValidMN ( c ) ;
if ( ! dmn ) {
debugMsg + = strprintf ( " %s (not in valid MN set anymore) \n " , c . ToString ( ) ) ;
} else {
debugMsg + = strprintf ( " %s (%s) \n " , c . ToString ( ) , dmn - > pdmnState - > addr . ToString ( false ) ) ;
}
}
2020-07-29 13:50:36 +02:00
LogPrint ( BCLog : : NET_NETCONN , debugMsg . c_str ( ) ) ; /* Continued */
2020-03-17 10:04:31 +01:00
}
g_connman - > AddPendingProbeConnections ( probeConnections ) ;
}
}
2019-01-15 15:35:26 +01:00
bool CLLMQUtils : : IsQuorumActive ( Consensus : : LLMQType llmqType , const uint256 & quorumHash )
{
auto & params = Params ( ) . GetConsensus ( ) . llmqs . at ( llmqType ) ;
// sig shares and recovered sigs are only accepted from recent/active quorums
// we allow one more active quorum as specified in consensus, as otherwise there is a small window where things could
// fail while we are on the brink of a new quorum
auto quorums = quorumManager - > ScanQuorums ( llmqType , ( int ) params . signingActiveQuorumCount + 1 ) ;
for ( auto & q : quorums ) {
2019-04-04 10:08:32 +02:00
if ( q - > qc . quorumHash = = quorumHash ) {
2019-01-15 15:35:26 +01:00
return true ;
}
}
return false ;
}
2020-12-10 00:08:05 +01:00
bool CLLMQUtils : : IsQuorumTypeEnabled ( Consensus : : LLMQType llmqType , const CBlockIndex * pindex )
{
2020-12-29 11:30:50 +01:00
LOCK ( cs_llmq_vbc ) ;
2020-12-10 00:08:05 +01:00
const Consensus : : Params & consensusParams = Params ( ) . GetConsensus ( ) ;
2020-12-29 11:30:50 +01:00
bool f_v17_Active = VersionBitsState ( pindex , consensusParams , Consensus : : DEPLOYMENT_V17 , llmq_versionbitscache ) = = ThresholdState : : ACTIVE ;
2020-12-10 00:08:05 +01:00
switch ( llmqType )
{
case Consensus : : LLMQ_50_60 :
case Consensus : : LLMQ_400_60 :
case Consensus : : LLMQ_400_85 :
break ;
case Consensus : : LLMQ_100_67 :
case Consensus : : LLMQ_TEST_V17 :
if ( ! f_v17_Active ) {
return false ;
}
break ;
case Consensus : : LLMQ_TEST :
case Consensus : : LLMQ_DEVNET :
break ;
default :
throw std : : runtime_error ( strprintf ( " %s: Unknown LLMQ type %d " , __func__ , llmqType ) ) ;
}
return true ;
}
std : : vector < Consensus : : LLMQType > CLLMQUtils : : GetEnabledQuorumTypes ( const CBlockIndex * pindex )
{
std : : vector < Consensus : : LLMQType > ret ;
for ( const auto & p : Params ( ) . GetConsensus ( ) . llmqs ) {
if ( IsQuorumTypeEnabled ( p . first , pindex ) ) {
ret . push_back ( p . first ) ;
}
}
return ret ;
}
2021-01-18 01:27:29 +01:00
const Consensus : : LLMQParams & GetLLMQParams ( Consensus : : LLMQType llmqType )
2020-12-10 00:08:05 +01:00
{
return Params ( ) . GetConsensus ( ) . llmqs . at ( llmqType ) ;
}
2019-01-15 15:35:26 +01:00
2019-07-15 20:55:01 +02:00
} // namespace llmq