2021-04-20 21:33:02 +02:00
// Copyright (c) 2019-2021 The Dash Core developers
2019-01-22 14:20:32 +01:00
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2021-10-02 19:32:24 +02:00
# include <llmq/chainlocks.h>
2021-04-16 05:41:16 +02:00
# include <llmq/quorums.h>
2021-10-02 19:32:24 +02:00
# include <llmq/instantsend.h>
# include <llmq/utils.h>
2020-03-19 23:46:56 +01:00
# include <chain.h>
2021-10-25 15:55:34 +02:00
# include <chainparams.h>
2021-04-16 05:41:16 +02:00
# include <consensus/validation.h>
2021-10-01 21:19:08 +02:00
# include <masternode/sync.h>
2020-03-19 23:46:56 +01:00
# include <net_processing.h>
# include <scheduler.h>
# include <spork.h>
# include <txmempool.h>
2021-08-10 21:41:00 +02:00
# include <ui_interface.h>
2021-06-25 08:07:28 +02:00
# include <util/validation.h>
2019-01-22 14:20:32 +01:00
namespace llmq
{
2021-04-26 13:51:15 +02:00
const std : : string CLSIG_REQUESTID_PREFIX = " clsig " ;
2019-01-22 14:20:32 +01:00
CChainLocksHandler * chainLocksHandler ;
2019-09-23 20:36:55 +02:00
bool CChainLockSig : : IsNull ( ) const
{
return nHeight = = - 1 & & blockHash = = uint256 ( ) ;
}
2019-01-22 14:20:32 +01:00
std : : string CChainLockSig : : ToString ( ) const
{
return strprintf ( " CChainLockSig(nHeight=%d, blockHash=%s) " , nHeight, blockHash.ToString()) ;
}
2020-06-09 05:53:16 +02:00
CChainLocksHandler : : CChainLocksHandler ( )
2019-01-22 14:20:32 +01:00
{
2021-10-28 21:11:34 +02:00
scheduler = std : : make_unique < CScheduler > ( ) ;
CScheduler : : Function serviceLoop = std : : bind ( & CScheduler : : serviceQueue , scheduler . get ( ) ) ;
scheduler_thread = std : : make_unique < std : : thread > ( std : : bind ( & TraceThread < CScheduler : : Function > , " cl-schdlr " , serviceLoop ) ) ;
2019-01-22 14:20:32 +01:00
}
CChainLocksHandler : : ~ CChainLocksHandler ( )
2019-02-17 12:39:43 +01:00
{
2020-03-06 20:47:49 +01:00
scheduler - > stop ( ) ;
2020-06-11 10:39:23 +02:00
scheduler_thread - > join ( ) ;
2019-02-17 12:39:43 +01:00
}
2019-02-28 15:02:20 +01:00
void CChainLocksHandler : : Start ( )
2019-02-17 12:39:43 +01:00
{
quorumSigningManager - > RegisterRecoveredSigsListener ( this ) ;
2019-02-28 15:02:20 +01:00
scheduler - > scheduleEvery ( [ & ] ( ) {
2019-03-22 11:51:50 +01:00
CheckActiveState ( ) ;
2019-03-13 14:00:54 +01:00
EnforceBestChainLock ( ) ;
2020-08-09 23:35:02 +02:00
// regularly retry signing the current chaintip as it might have failed before due to missing islocks
2019-02-28 15:02:20 +01:00
TrySignChainTip ( ) ;
} , 5000 ) ;
2019-02-17 12:39:43 +01:00
}
2019-02-28 15:02:20 +01:00
void CChainLocksHandler : : Stop ( )
2019-01-22 14:20:32 +01:00
{
2020-06-11 10:39:23 +02:00
scheduler - > stop ( ) ;
2019-01-22 14:20:32 +01:00
quorumSigningManager - > UnregisterRecoveredSigsListener ( this ) ;
}
2021-09-08 00:33:02 +02:00
bool CChainLocksHandler : : AlreadyHave ( const CInv & inv ) const
2019-01-22 14:20:32 +01:00
{
LOCK ( cs ) ;
return seenChainLocks . count ( inv . hash ) ! = 0 ;
}
2021-09-08 00:33:02 +02:00
bool CChainLocksHandler : : GetChainLockByHash ( const uint256 & hash , llmq : : CChainLockSig & ret ) const
2019-01-22 14:20:32 +01:00
{
LOCK ( cs ) ;
if ( hash ! = bestChainLockHash ) {
// we only propagate the best one and ditch all the old ones
return false ;
}
ret = bestChainLock ;
return true ;
}
2021-09-08 00:33:02 +02:00
CChainLockSig CChainLocksHandler : : GetBestChainLock ( ) const
2019-09-23 20:36:55 +02:00
{
LOCK ( cs ) ;
return bestChainLock ;
}
2021-01-11 04:28:37 +01:00
void CChainLocksHandler : : ProcessMessage ( CNode * pfrom , const std : : string & strCommand , CDataStream & vRecv )
2019-01-22 14:20:32 +01:00
{
2020-12-09 21:33:09 +01:00
if ( ! AreChainLocksEnabled ( ) ) {
2019-01-23 17:40:37 +01:00
return ;
}
2019-01-22 14:20:32 +01:00
if ( strCommand = = NetMsgType : : CLSIG ) {
CChainLockSig clsig ;
vRecv > > clsig ;
2021-08-06 23:55:51 +02:00
ProcessNewChainLock ( pfrom - > GetId ( ) , clsig , : : SerializeHash ( clsig ) ) ;
2019-01-22 14:20:32 +01:00
}
}
2021-02-14 23:12:40 +01:00
void CChainLocksHandler : : ProcessNewChainLock ( const NodeId from , const llmq : : CChainLockSig & clsig , const uint256 & hash )
2019-01-22 14:20:32 +01:00
{
2021-03-18 00:45:41 +01:00
CheckActiveState ( ) ;
2021-02-14 23:12:40 +01:00
CInv clsigInv ( MSG_CLSIG , hash ) ;
2020-10-28 20:02:05 +01:00
if ( from ! = - 1 ) {
2019-02-27 14:10:12 +01:00
LOCK ( cs_main ) ;
2021-02-14 23:12:40 +01:00
EraseObjectRequest ( from , clsigInv ) ;
2019-02-27 14:10:12 +01:00
}
2019-01-22 14:20:32 +01:00
{
LOCK ( cs ) ;
if ( ! seenChainLocks . emplace ( hash , GetTimeMillis ( ) ) . second ) {
return ;
}
2019-09-23 20:36:55 +02:00
if ( ! bestChainLock . IsNull ( ) & & clsig . nHeight < = bestChainLock . nHeight ) {
2019-01-22 14:20:32 +01:00
// no need to process/relay older CLSIGs
return ;
}
}
2021-02-14 23:12:40 +01:00
const uint256 requestId = : : SerializeHash ( std : : make_pair ( CLSIG_REQUESTID_PREFIX , clsig . nHeight ) ) ;
2021-06-26 15:10:53 +02:00
if ( ! llmq : : CSigningManager : : VerifyRecoveredSig ( Params ( ) . GetConsensus ( ) . llmqTypeChainLocks , clsig . nHeight , requestId , clsig . blockHash , clsig . sig ) ) {
2020-01-28 11:04:47 +01:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d \n " , __func__ , clsig . ToString ( ) , from ) ;
2019-01-22 14:20:32 +01:00
if ( from ! = - 1 ) {
LOCK ( cs_main ) ;
2019-03-06 08:00:21 +01:00
Misbehaving ( from , 10 ) ;
2019-01-22 14:20:32 +01:00
}
return ;
}
2021-09-28 23:40:32 +02:00
CBlockIndex * pindex = WITH_LOCK ( cs_main , return LookupBlockIndex ( clsig . blockHash ) ) ;
2021-02-22 23:47:13 +01:00
{
LOCK ( cs ) ;
2019-01-22 14:20:32 +01:00
bestChainLockHash = hash ;
bestChainLock = clsig ;
2021-02-22 23:47:13 +01:00
if ( pindex ! = nullptr ) {
2021-02-16 16:54:19 +01:00
2021-02-22 23:47:13 +01:00
if ( pindex - > nHeight ! = clsig . nHeight ) {
// Should not happen, same as the conflict check from above.
LogPrintf ( " CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d) \n " ,
__func__ , clsig . ToString ( ) , pindex - > nHeight ) ;
// Note: not relaying clsig here
return ;
}
2019-01-22 14:20:32 +01:00
2021-02-22 23:47:13 +01:00
bestChainLockWithKnownBlock = bestChainLock ;
bestChainLockBlockIndex = pindex ;
2019-01-22 14:20:32 +01:00
}
2021-02-22 23:47:13 +01:00
// else if (pindex == nullptr)
// Note: make sure to still relay clsig further.
}
2019-01-22 14:20:32 +01:00
2021-02-22 23:47:13 +01:00
// Note: do not hold cs while calling RelayInv
AssertLockNotHeld ( cs ) ;
g_connman - > RelayInv ( clsigInv , LLMQS_PROTO_VERSION ) ;
if ( pindex = = nullptr ) {
// we don't know the block/header for this CLSIG yet, so bail out for now
// when the block or the header later comes in, we will enforce the correct chain
return ;
2019-01-22 14:20:32 +01:00
}
2019-03-13 14:00:54 +01:00
scheduler - > scheduleFromNow ( [ & ] ( ) {
2019-03-22 11:51:50 +01:00
CheckActiveState ( ) ;
2019-03-13 14:00:54 +01:00
EnforceBestChainLock ( ) ;
} , 0 ) ;
2019-01-22 14:20:32 +01:00
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d \n " ,
2019-01-22 14:20:32 +01:00
__func__ , clsig . ToString ( ) , from ) ;
}
void CChainLocksHandler : : AcceptedBlockHeader ( const CBlockIndex * pindexNew )
{
2021-03-08 20:50:15 +01:00
LOCK ( cs ) ;
2019-01-22 14:20:32 +01:00
2019-03-13 14:00:54 +01:00
if ( pindexNew - > GetBlockHash ( ) = = bestChainLock . blockHash ) {
2021-04-17 21:23:54 +02:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- block header %s came in late, updating and enforcing \n " , __func__ , pindexNew - > GetBlockHash ( ) . ToString ( ) ) ;
2019-01-22 14:20:32 +01:00
2019-03-13 14:00:54 +01:00
if ( bestChainLock . nHeight ! = pindexNew - > nHeight ) {
// Should not happen, same as the conflict check from ProcessNewChainLock.
LogPrintf ( " CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d) \n " ,
__func__ , bestChainLock . ToString ( ) , pindexNew - > nHeight ) ;
return ;
2019-01-22 14:20:32 +01:00
}
2019-03-13 14:00:54 +01:00
// when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the
// CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual
// block processing logic will handle this when the block arrives
2019-03-22 11:51:50 +01:00
bestChainLockWithKnownBlock = bestChainLock ;
2019-03-13 14:00:54 +01:00
bestChainLockBlockIndex = pindexNew ;
2019-01-22 14:20:32 +01:00
}
}
2021-11-29 06:12:09 +01:00
void CChainLocksHandler : : UpdatedBlockTip ( )
2019-01-22 14:20:32 +01:00
{
2019-02-28 15:02:20 +01:00
// don't call TrySignChainTip directly but instead let the scheduler call it. This way we ensure that cs_main is
2019-03-13 14:00:54 +01:00
// never locked and TrySignChainTip is not called twice in parallel. Also avoids recursive calls due to
// EnforceBestChainLock switching chains.
2021-11-29 06:06:50 +01:00
// atomic[If tryLockChainTipScheduled is false, do (set it to true] and schedule signing).
if ( bool expected = false ; tryLockChainTipScheduled . compare_exchange_strong ( expected , true ) ) {
scheduler - > scheduleFromNow ( [ & ] ( ) {
CheckActiveState ( ) ;
EnforceBestChainLock ( ) ;
TrySignChainTip ( ) ;
tryLockChainTipScheduled = false ;
} , 0 ) ;
2019-02-28 15:02:20 +01:00
}
}
2019-03-22 11:51:50 +01:00
void CChainLocksHandler : : CheckActiveState ( )
{
bool fDIP0008Active ;
{
LOCK ( cs_main ) ;
2021-10-16 12:54:22 +02:00
fDIP0008Active = : : ChainActive ( ) . Tip ( ) & & : : ChainActive ( ) . Tip ( ) - > pprev & & : : ChainActive ( ) . Tip ( ) - > pprev - > nHeight > = Params ( ) . GetConsensus ( ) . DIP0008Height ;
2019-03-22 11:51:50 +01:00
}
bool oldIsEnforced = isEnforced ;
2020-12-09 21:33:09 +01:00
isEnabled = AreChainLocksEnabled ( ) ;
isEnforced = ( fDIP0008Active & & isEnabled ) ;
2019-03-22 11:51:50 +01:00
if ( ! oldIsEnforced & & isEnforced ) {
// ChainLocks got activated just recently, but it's possible that it was already running before, leaving
2019-04-11 14:41:51 +02:00
// us with some stale values which we should not try to enforce anymore (there probably was a good reason
2019-03-22 11:51:50 +01:00
// to disable spork19)
2021-09-28 23:07:01 +02:00
LOCK ( cs ) ;
2019-03-22 11:51:50 +01:00
bestChainLockHash = uint256 ( ) ;
bestChainLock = bestChainLockWithKnownBlock = CChainLockSig ( ) ;
bestChainLockBlockIndex = lastNotifyChainLockBlockIndex = nullptr ;
}
}
2019-02-28 15:02:20 +01:00
void CChainLocksHandler : : TrySignChainTip ( )
{
Cleanup ( ) ;
2019-04-30 14:55:11 +02:00
if ( ! fMasternodeMode ) {
return ;
}
if ( ! masternodeSync . IsBlockchainSynced ( ) ) {
return ;
}
2021-09-28 23:07:01 +02:00
if ( ! isEnabled ) {
return ;
}
2019-02-28 15:02:20 +01:00
const CBlockIndex * pindex ;
{
LOCK ( cs_main ) ;
2021-10-16 12:54:22 +02:00
pindex = : : ChainActive ( ) . Tip ( ) ;
2019-02-28 15:02:20 +01:00
}
if ( ! pindex - > pprev ) {
2019-01-22 14:20:32 +01:00
return ;
}
// DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
// To simplify the initial implementation, we skip this process and directly try to create a CLSIG
// This will fail when multiple blocks compete, but we accept this for the initial implementation.
// Later, we'll add the multiple attempts process.
{
LOCK ( cs ) ;
2019-03-01 06:45:11 +01:00
if ( pindex - > nHeight = = lastSignedHeight ) {
// already signed this one
return ;
}
if ( bestChainLock . nHeight > = pindex - > nHeight ) {
// already got the same CLSIG or a better one
return ;
}
2019-02-28 15:02:20 +01:00
if ( InternalHasConflictingChainLock ( pindex - > nHeight , pindex - > GetBlockHash ( ) ) ) {
2019-03-13 14:00:54 +01:00
// don't sign if another conflicting CLSIG is already present. EnforceBestChainLock will later enforce
// the correct chain.
2019-01-22 14:20:32 +01:00
return ;
}
2019-03-01 06:45:11 +01:00
}
2019-01-22 14:20:32 +01:00
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- trying to sign %s, height=%d \n " , __func__ , pindex - > GetBlockHash ( ) . ToString ( ) , pindex - > nHeight ) ;
2019-03-01 06:46:31 +01:00
// When the new IX system is activated, we only try to ChainLock blocks which include safe transactions. A TX is
2020-08-09 23:35:02 +02:00
// considered safe when it is islocked or at least known since 10 minutes (from mempool or block). These checks are
2019-03-01 06:46:31 +01:00
// performed for the tip (which we try to sign) and the previous 5 blocks. If a ChainLocked block is found on the
// way down, we consider all TXs to be safe.
2020-12-09 21:33:09 +01:00
if ( IsInstantSendEnabled ( ) & & RejectConflictingBlocks ( ) ) {
2019-03-01 06:46:31 +01:00
auto pindexWalk = pindex ;
while ( pindexWalk ) {
if ( pindex - > nHeight - pindexWalk - > nHeight > 5 ) {
// no need to check further down, 6 confs is safe to assume that TXs below this height won't be
2020-08-09 23:35:02 +02:00
// islocked anymore if they aren't already
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- tip and previous 5 blocks all safe \n " , __func__ ) ;
2019-03-01 06:46:31 +01:00
break ;
}
if ( HasChainLock ( pindexWalk - > nHeight , pindexWalk - > GetBlockHash ( ) ) ) {
2020-08-09 23:35:02 +02:00
// we don't care about islocks for TXs that are ChainLocked already
2021-03-26 13:11:11 +01:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- chainlock at height %d \n " , __func__ , pindexWalk - > nHeight ) ;
2019-03-01 06:46:31 +01:00
break ;
}
2019-05-10 15:54:40 +02:00
auto txids = GetBlockTxs ( pindexWalk - > GetBlockHash ( ) ) ;
if ( ! txids ) {
pindexWalk = pindexWalk - > pprev ;
continue ;
2019-03-01 06:46:31 +01:00
}
for ( auto & txid : * txids ) {
int64_t txAge = 0 ;
{
LOCK ( cs ) ;
auto it = txFirstSeenTime . find ( txid ) ;
if ( it ! = txFirstSeenTime . end ( ) ) {
txAge = GetAdjustedTime ( ) - it - > second ;
}
}
2019-03-04 10:58:47 +01:00
if ( txAge < WAIT_FOR_ISLOCK_TIMEOUT & & ! quorumInstantSendManager - > IsLocked ( txid ) ) {
2020-08-09 23:35:02 +02:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- not signing block %s due to TX %s not being islocked and not old enough. age=%d \n " , __func__ ,
2019-03-01 06:46:31 +01:00
pindexWalk - > GetBlockHash ( ) . ToString ( ) , txid . ToString ( ) , txAge ) ;
return ;
}
}
pindexWalk = pindexWalk - > pprev ;
}
}
2019-03-01 06:45:11 +01:00
uint256 requestId = : : SerializeHash ( std : : make_pair ( CLSIG_REQUESTID_PREFIX , pindex - > nHeight ) ) ;
uint256 msgHash = pindex - > GetBlockHash ( ) ;
2019-01-22 14:20:32 +01:00
2019-03-01 06:45:11 +01:00
{
LOCK ( cs ) ;
if ( bestChainLock . nHeight > = pindex - > nHeight ) {
// might have happened while we didn't hold cs
2019-01-22 14:20:32 +01:00
return ;
}
2019-02-28 15:02:20 +01:00
lastSignedHeight = pindex - > nHeight ;
2019-01-22 14:20:32 +01:00
lastSignedRequestId = requestId ;
lastSignedMsgHash = msgHash ;
}
2019-09-24 11:38:50 +02:00
quorumSigningManager - > AsyncSignIfMember ( Params ( ) . GetConsensus ( ) . llmqTypeChainLocks , requestId , msgHash ) ;
2019-01-22 14:20:32 +01:00
}
2019-12-07 11:56:17 +01:00
void CChainLocksHandler : : TransactionAddedToMempool ( const CTransactionRef & tx , int64_t nAcceptTime )
2019-05-27 13:48:01 +02:00
{
2019-05-27 13:56:04 +02:00
if ( tx - > IsCoinBase ( ) | | tx - > vin . empty ( ) ) {
return ;
}
LOCK ( cs ) ;
2019-12-07 11:56:17 +01:00
txFirstSeenTime . emplace ( tx - > GetHash ( ) , nAcceptTime ) ;
2019-05-27 13:48:01 +02:00
}
void CChainLocksHandler : : BlockConnected ( const std : : shared_ptr < const CBlock > & pblock , const CBlockIndex * pindex , const std : : vector < CTransactionRef > & vtxConflicted )
{
2019-05-27 13:52:30 +02:00
if ( ! masternodeSync . IsBlockchainSynced ( ) ) {
return ;
}
// We listen for BlockConnected so that we can collect all TX ids of all included TXs of newly received blocks
// We need this information later when we try to sign a new tip, so that we can determine if all included TXs are
// safe.
LOCK ( cs ) ;
auto it = blockTxs . find ( pindex - > GetBlockHash ( ) ) ;
if ( it = = blockTxs . end ( ) ) {
// we must create this entry even if there are no lockable transactions in the block, so that TrySignChainTip
// later knows about this block
it = blockTxs . emplace ( pindex - > GetBlockHash ( ) , std : : make_shared < std : : unordered_set < uint256 , StaticSaltedHasher > > ( ) ) . first ;
}
auto & txids = * it - > second ;
2019-05-27 13:56:04 +02:00
int64_t curTime = GetAdjustedTime ( ) ;
2019-05-27 13:52:30 +02:00
for ( const auto & tx : pblock - > vtx ) {
if ( tx - > IsCoinBase ( ) | | tx - > vin . empty ( ) ) {
continue ;
}
txids . emplace ( tx - > GetHash ( ) ) ;
2019-05-27 13:56:04 +02:00
txFirstSeenTime . emplace ( tx - > GetHash ( ) , curTime ) ;
2019-05-27 13:52:30 +02:00
}
2019-05-27 13:48:01 +02:00
}
void CChainLocksHandler : : BlockDisconnected ( const std : : shared_ptr < const CBlock > & pblock , const CBlockIndex * pindexDisconnected )
{
2019-05-27 13:52:30 +02:00
LOCK ( cs ) ;
blockTxs . erase ( pindexDisconnected - > GetBlockHash ( ) ) ;
2019-05-27 13:48:01 +02:00
}
2019-05-10 15:54:40 +02:00
CChainLocksHandler : : BlockTxs : : mapped_type CChainLocksHandler : : GetBlockTxs ( const uint256 & blockHash )
{
AssertLockNotHeld ( cs ) ;
AssertLockNotHeld ( cs_main ) ;
CChainLocksHandler : : BlockTxs : : mapped_type ret ;
{
LOCK ( cs ) ;
auto it = blockTxs . find ( blockHash ) ;
if ( it ! = blockTxs . end ( ) ) {
ret = it - > second ;
}
}
if ( ! ret ) {
// This should only happen when freshly started.
// If running for some time, SyncTransaction should have been called before which fills blockTxs.
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- blockTxs for %s not found. Trying ReadBlockFromDisk \n " , __func__ ,
2019-05-10 15:54:40 +02:00
blockHash . ToString ( ) ) ;
uint32_t blockTime ;
{
LOCK ( cs_main ) ;
2018-03-13 19:04:28 +01:00
auto pindex = LookupBlockIndex ( blockHash ) ;
2019-05-10 15:54:40 +02:00
CBlock block ;
if ( ! ReadBlockFromDisk ( block , pindex , Params ( ) . GetConsensus ( ) ) ) {
return nullptr ;
}
ret = std : : make_shared < std : : unordered_set < uint256 , StaticSaltedHasher > > ( ) ;
for ( auto & tx : block . vtx ) {
if ( tx - > IsCoinBase ( ) | | tx - > vin . empty ( ) ) {
continue ;
}
ret - > emplace ( tx - > GetHash ( ) ) ;
}
blockTime = block . nTime ;
}
LOCK ( cs ) ;
blockTxs . emplace ( blockHash , ret ) ;
for ( auto & txid : * ret ) {
txFirstSeenTime . emplace ( txid , blockTime ) ;
}
}
return ret ;
}
2021-09-08 00:33:02 +02:00
bool CChainLocksHandler : : IsTxSafeForMining ( const uint256 & txid ) const
2019-03-01 06:46:31 +01:00
{
2020-12-09 21:33:09 +01:00
if ( ! RejectConflictingBlocks ( ) ) {
2019-03-01 06:46:31 +01:00
return true ;
}
2021-11-29 06:12:09 +01:00
if ( ! isEnabled | | ! isEnforced ) {
return true ;
}
2019-07-09 16:50:08 +02:00
if ( ! IsInstantSendEnabled ( ) ) {
2019-03-01 06:46:31 +01:00
return true ;
}
2021-11-29 06:12:09 +01:00
if ( quorumInstantSendManager - > IsLocked ( txid ) ) {
2021-09-28 23:07:01 +02:00
return true ;
}
2019-03-01 06:46:31 +01:00
int64_t txAge = 0 ;
{
LOCK ( cs ) ;
auto it = txFirstSeenTime . find ( txid ) ;
if ( it ! = txFirstSeenTime . end ( ) ) {
txAge = GetAdjustedTime ( ) - it - > second ;
}
}
2021-11-29 06:12:09 +01:00
return txAge > = WAIT_FOR_ISLOCK_TIMEOUT ;
2019-03-01 06:46:31 +01:00
}
2019-01-22 14:20:32 +01:00
// WARNING: cs_main and cs should not be held!
2019-03-13 14:00:54 +01:00
// This should also not be called from validation signals, as this might result in recursive calls
2019-01-22 14:20:32 +01:00
void CChainLocksHandler : : EnforceBestChainLock ( )
{
2020-11-28 20:06:51 +01:00
AssertLockNotHeld ( cs ) ;
AssertLockNotHeld ( cs_main ) ;
2021-01-22 05:32:15 +01:00
std : : shared_ptr < CChainLockSig > clsig ;
2019-01-22 14:20:32 +01:00
const CBlockIndex * pindex ;
2019-03-13 14:00:54 +01:00
const CBlockIndex * currentBestChainLockBlockIndex ;
2019-01-22 14:20:32 +01:00
{
LOCK ( cs ) ;
2019-03-22 11:51:50 +01:00
if ( ! isEnforced ) {
return ;
}
2021-01-22 05:32:15 +01:00
clsig = std : : make_shared < CChainLockSig > ( bestChainLockWithKnownBlock ) ;
2019-03-13 14:00:54 +01:00
pindex = currentBestChainLockBlockIndex = this - > bestChainLockBlockIndex ;
if ( ! currentBestChainLockBlockIndex ) {
// we don't have the header/block, so we can't do anything right now
return ;
}
2019-01-22 14:20:32 +01:00
}
2021-01-16 20:47:13 +01:00
CValidationState state ;
const auto & params = Params ( ) ;
2019-01-22 14:20:32 +01:00
2021-10-25 14:22:30 +02:00
// Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
// For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
// and mark all of them as conflicting.
LogPrint ( BCLog : : CHAINLOCKS , " CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s) \n " , __func__ , pindex - > GetBlockHash ( ) . ToString ( ) , clsig - > ToString ( ) ) ;
EnforceBlock ( state , params , pindex ) ;
2019-03-13 14:00:54 +01:00
2021-10-25 14:22:30 +02:00
bool activateNeeded = WITH_LOCK ( : : cs_main , return : : ChainActive ( ) . Tip ( ) - > GetAncestor ( currentBestChainLockBlockIndex - > nHeight ) ) ! = currentBestChainLockBlockIndex ;
2019-01-22 14:20:32 +01:00
2021-06-23 10:01:37 +02:00
if ( activateNeeded ) {
if ( ! ActivateBestChain ( state , params ) ) {
LogPrintf ( " CChainLocksHandler::%s -- ActivateBestChain failed: %s \n " , __func__ , FormatStateMessage ( state ) ) ;
return ;
}
2019-03-13 14:00:54 +01:00
LOCK ( cs_main ) ;
2021-10-16 12:54:22 +02:00
if ( : : ChainActive ( ) . Tip ( ) - > GetAncestor ( currentBestChainLockBlockIndex - > nHeight ) ! = currentBestChainLockBlockIndex ) {
2021-06-23 10:01:37 +02:00
return ;
2019-03-13 14:00:54 +01:00
}
}
2021-06-23 10:01:37 +02:00
{
LOCK ( cs ) ;
if ( lastNotifyChainLockBlockIndex = = currentBestChainLockBlockIndex ) return ;
lastNotifyChainLockBlockIndex = currentBestChainLockBlockIndex ;
2019-01-22 14:20:32 +01:00
}
2021-06-23 10:01:37 +02:00
GetMainSignals ( ) . NotifyChainLock ( currentBestChainLockBlockIndex , clsig ) ;
2021-08-10 21:41:00 +02:00
uiInterface . NotifyChainLock ( clsig - > blockHash . ToString ( ) , clsig - > nHeight ) ;
2019-01-22 14:20:32 +01:00
}
void CChainLocksHandler : : HandleNewRecoveredSig ( const llmq : : CRecoveredSig & recoveredSig )
{
2021-09-28 23:07:01 +02:00
if ( ! isEnabled ) {
return ;
}
2019-01-22 14:20:32 +01:00
CChainLockSig clsig ;
{
LOCK ( cs ) ;
if ( recoveredSig . id ! = lastSignedRequestId | | recoveredSig . msgHash ! = lastSignedMsgHash ) {
// this is not what we signed, so lets not create a CLSIG for it
return ;
}
if ( bestChainLock . nHeight > = lastSignedHeight ) {
// already got the same or a better CLSIG through the CLSIG message
return ;
}
clsig . nHeight = lastSignedHeight ;
clsig . blockHash = lastSignedMsgHash ;
2019-06-13 11:01:26 +02:00
clsig . sig = recoveredSig . sig . Get ( ) ;
2019-01-22 14:20:32 +01:00
}
ProcessNewChainLock ( - 1 , clsig , : : SerializeHash ( clsig ) ) ;
}
2021-09-08 00:33:02 +02:00
bool CChainLocksHandler : : HasChainLock ( int nHeight , const uint256 & blockHash ) const
2019-01-22 14:20:32 +01:00
{
LOCK ( cs ) ;
return InternalHasChainLock ( nHeight , blockHash ) ;
}
2021-09-08 00:33:02 +02:00
bool CChainLocksHandler : : InternalHasChainLock ( int nHeight , const uint256 & blockHash ) const
2019-01-22 14:20:32 +01:00
{
AssertLockHeld ( cs ) ;
2019-03-22 11:51:50 +01:00
if ( ! isEnforced ) {
return false ;
}
2019-01-22 14:20:32 +01:00
if ( ! bestChainLockBlockIndex ) {
return false ;
}
if ( nHeight > bestChainLockBlockIndex - > nHeight ) {
return false ;
}
if ( nHeight = = bestChainLockBlockIndex - > nHeight ) {
return blockHash = = bestChainLockBlockIndex - > GetBlockHash ( ) ;
}
auto pAncestor = bestChainLockBlockIndex - > GetAncestor ( nHeight ) ;
return pAncestor & & pAncestor - > GetBlockHash ( ) = = blockHash ;
}
2021-09-08 00:33:02 +02:00
bool CChainLocksHandler : : HasConflictingChainLock ( int nHeight , const uint256 & blockHash ) const
2019-01-22 14:20:32 +01:00
{
LOCK ( cs ) ;
return InternalHasConflictingChainLock ( nHeight , blockHash ) ;
}
2021-09-08 00:33:02 +02:00
bool CChainLocksHandler : : InternalHasConflictingChainLock ( int nHeight , const uint256 & blockHash ) const
2019-01-22 14:20:32 +01:00
{
AssertLockHeld ( cs ) ;
2019-03-22 11:51:50 +01:00
if ( ! isEnforced ) {
return false ;
}
2019-01-22 14:20:32 +01:00
if ( ! bestChainLockBlockIndex ) {
return false ;
}
if ( nHeight > bestChainLockBlockIndex - > nHeight ) {
return false ;
}
if ( nHeight = = bestChainLockBlockIndex - > nHeight ) {
return blockHash ! = bestChainLockBlockIndex - > GetBlockHash ( ) ;
}
auto pAncestor = bestChainLockBlockIndex - > GetAncestor ( nHeight ) ;
assert ( pAncestor ) ;
return pAncestor - > GetBlockHash ( ) ! = blockHash ;
}
void CChainLocksHandler : : Cleanup ( )
{
2019-04-30 14:55:11 +02:00
if ( ! masternodeSync . IsBlockchainSynced ( ) ) {
return ;
}
2019-01-22 14:20:32 +01:00
{
LOCK ( cs ) ;
if ( GetTimeMillis ( ) - lastCleanupTime < CLEANUP_INTERVAL ) {
return ;
}
}
2019-02-28 15:04:27 +01:00
// need mempool.cs due to GetTransaction calls
LOCK2 ( cs_main , mempool . cs ) ;
LOCK ( cs ) ;
2019-01-22 14:20:32 +01:00
for ( auto it = seenChainLocks . begin ( ) ; it ! = seenChainLocks . end ( ) ; ) {
if ( GetTimeMillis ( ) - it - > second > = CLEANUP_SEEN_TIMEOUT ) {
it = seenChainLocks . erase ( it ) ;
} else {
+ + it ;
}
}
2019-02-28 15:04:27 +01:00
for ( auto it = blockTxs . begin ( ) ; it ! = blockTxs . end ( ) ; ) {
2018-03-13 19:04:28 +01:00
auto pindex = LookupBlockIndex ( it - > first ) ;
2019-02-28 15:04:27 +01:00
if ( InternalHasChainLock ( pindex - > nHeight , pindex - > GetBlockHash ( ) ) ) {
for ( auto & txid : * it - > second ) {
txFirstSeenTime . erase ( txid ) ;
}
it = blockTxs . erase ( it ) ;
} else if ( InternalHasConflictingChainLock ( pindex - > nHeight , pindex - > GetBlockHash ( ) ) ) {
it = blockTxs . erase ( it ) ;
} else {
+ + it ;
}
}
for ( auto it = txFirstSeenTime . begin ( ) ; it ! = txFirstSeenTime . end ( ) ; ) {
CTransactionRef tx ;
uint256 hashBlock ;
if ( ! GetTransaction ( it - > first , tx , Params ( ) . GetConsensus ( ) , hashBlock ) ) {
// tx has vanished, probably due to conflicts
it = txFirstSeenTime . erase ( it ) ;
} else if ( ! hashBlock . IsNull ( ) ) {
2018-03-13 19:04:28 +01:00
auto pindex = LookupBlockIndex ( hashBlock ) ;
2021-10-16 12:54:22 +02:00
if ( : : ChainActive ( ) . Tip ( ) - > GetAncestor ( pindex - > nHeight ) = = pindex & & : : ChainActive ( ) . Height ( ) - pindex - > nHeight > = 6 ) {
2019-02-28 15:04:27 +01:00
// tx got confirmed >= 6 times, so we can stop keeping track of it
it = txFirstSeenTime . erase ( it ) ;
} else {
+ + it ;
}
} else {
+ + it ;
}
}
2019-01-22 14:20:32 +01:00
lastCleanupTime = GetTimeMillis ( ) ;
}
2020-12-09 21:33:09 +01:00
bool AreChainLocksEnabled ( )
{
return sporkManager . IsSporkActive ( SPORK_19_CHAINLOCKS_ENABLED ) ;
}
2019-07-15 20:55:01 +02:00
} // namespace llmq