LCOV - code coverage report
Current view: top level - src/llmq - quorums_chainlocks.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 190 220 86.4 %
Date: 2025-02-23 09:33:43 Functions: 23 24 95.8 %

          Line data    Source code
       1             : // Copyright (c) 2019 The Dash Core developers
       2             : // Copyright (c) 2023 The PIVX Core developers
       3             : // Distributed under the MIT/X11 software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "quorums.h"
       7             : #include "quorums_chainlocks.h"
       8             : #include "quorums_signing.h"
       9             : #include "quorums_utils.h"
      10             : 
      11             : #include "chain.h"
      12             : #include "net_processing.h"
      13             : #include "scheduler.h"
      14             : #include "spork.h"
      15             : #include "sporkid.h"
      16             : #include "tiertwo/tiertwo_sync_state.h"
      17             : #include "validation.h"
      18             : 
      19             : namespace llmq
      20             : {
      21             : 
      22             : static const std::string CLSIG_REQUESTID_PREFIX = "clsig";
      23             : 
      24             : std::unique_ptr<CChainLocksHandler> chainLocksHandler{nullptr};
      25             : 
      26         836 : bool CChainLockSig::IsNull() const
      27             : {
      28         869 :     return nHeight == -1 && blockHash == uint256();
      29             : }
      30             : 
      31         837 : std::string CChainLockSig::ToString() const
      32             : {
      33        1674 :     return strprintf("CChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString());
      34             : }
      35             : 
      36         475 : CChainLocksHandler::CChainLocksHandler(CScheduler* _scheduler) :
      37        1900 :     scheduler(_scheduler)
      38             : {
      39         475 : }
      40             : 
      41         950 : CChainLocksHandler::~CChainLocksHandler()
      42             : {
      43         950 : }
      44             : 
      45         347 : void CChainLocksHandler::Start()
      46             : {
      47         347 :     quorumSigningManager->RegisterRecoveredSigsListener(this);
      48         347 :     scheduler->scheduleEvery([&]() {
      49        3525 :         EnforceBestChainLock();
      50             :         // regularely retry signing the current chaintip
      51        3525 :         TrySignChainTip();
      52             :     },
      53             :         5000);
      54         347 : }
      55             : 
      56         357 : void CChainLocksHandler::Stop()
      57             : {
      58         357 :     quorumSigningManager->UnregisterRecoveredSigsListener(this);
      59         357 : }
      60             : 
      61        9843 : bool CChainLocksHandler::AlreadyHave(const CInv& inv)
      62             : {
      63        9843 :     LOCK(cs);
      64       19686 :     return seenChainLocks.count(inv.hash) != 0;
      65             : }
      66             : 
      67         727 : bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, llmq::CChainLockSig& ret)
      68             : {
      69         727 :     LOCK(cs);
      70             : 
      71         727 :     if (hash != bestChainLockHash) {
      72             :         // we only propagate the best one and ditch all the old ones
      73             :         return false;
      74             :     }
      75             : 
      76         727 :     ret = bestChainLock;
      77         727 :     return true;
      78             : }
      79             : 
      80           0 : CChainLockSig CChainLocksHandler::GetBestChainLock()
      81             : {
      82           0 :     LOCK(cs);
      83           0 :     return bestChainLock;
      84             : }
      85             : 
      86         725 : void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
      87             : {
      88         725 :     if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
      89             :         return;
      90             :     }
      91             : 
      92         725 :     if (strCommand == NetMsgType::CLSIG) {
      93         725 :         CChainLockSig clsig;
      94         725 :         vRecv >> clsig;
      95             : 
      96         725 :         auto hash = ::SerializeHash(clsig);
      97             : 
      98         725 :         ProcessNewChainLock(pfrom->GetId(), clsig, hash);
      99             :     }
     100             : }
     101             : 
     102         842 : void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLockSig& clsig, const uint256& hash)
     103             : {
     104         842 :     {
     105         842 :         LOCK(cs_main);
     106         842 :         g_connman->RemoveAskFor(hash, MSG_CLSIG);
     107             :     }
     108             : 
     109         842 :     {
     110         842 :         LOCK(cs);
     111         842 :         if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) {
     112           7 :             return;
     113             :         }
     114             : 
     115         836 :         if (!bestChainLock.IsNull() && clsig.nHeight <= bestChainLock.nHeight) {
     116             :             // no need to process/relay older CLSIGs
     117             :             return;
     118             :         }
     119             :     }
     120             : 
     121         835 :     uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, clsig.nHeight));
     122         835 :     uint256 msgHash = clsig.blockHash;
     123         835 :     if (!quorumSigningManager->VerifyRecoveredSig(Params().GetConsensus().llmqTypeChainLocks, clsig.nHeight, requestId, msgHash, clsig.sig)) {
     124           0 :         LogPrintf("CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d\n", __func__, clsig.ToString(), from);
     125           0 :         if (from != -1) {
     126           0 :             LOCK(cs_main);
     127           0 :             Misbehaving(from, 10);
     128             :         }
     129           0 :         return;
     130             :     }
     131             : 
     132         835 :     {
     133        1670 :         LOCK2(cs_main, cs);
     134             : 
     135         835 :         if (InternalHasConflictingChainLock(clsig.nHeight, clsig.blockHash)) {
     136             :             // This should not happen. If it happens, it means that a malicious entity controls a large part of the MN
     137             :             // network. In this case, we don't allow him to reorg older chainlocks.
     138           0 :             LogPrintf("CChainLocksHandler::%s -- new CLSIG (%s) tries to reorg previous CLSIG (%s), peer=%d\n",
     139           0 :                       __func__, clsig.ToString(), bestChainLock.ToString(), from);
     140           0 :             return;
     141             :         }
     142             : 
     143         835 :         bestChainLockHash = hash;
     144         835 :         bestChainLock = clsig;
     145             : 
     146         835 :         CInv inv(MSG_CLSIG, hash);
     147         835 :         g_connman->RelayInv(inv, LLMQS_PROTO_VERSION);
     148             : 
     149         835 :         auto blockIt = mapBlockIndex.find(clsig.blockHash);
     150         835 :         if (blockIt == mapBlockIndex.end()) {
     151             :             // we don't know the block/header for this CLSIG yet, so bail out for now
     152             :             // when the block or the header later comes in, we will enforce the correct chain
     153             :             return;
     154             :         }
     155             : 
     156         835 :         if (blockIt->second->nHeight != clsig.nHeight) {
     157             :             // Should not happen, same as the conflict check from above.
     158           0 :             LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
     159           0 :                     __func__, clsig.ToString(), blockIt->second->nHeight);
     160           0 :             return;
     161             :         }
     162             : 
     163         835 :         const CBlockIndex* pindex = blockIt->second;
     164         835 :         bestChainLockWithKnownBlock = bestChainLock;
     165         835 :         bestChainLockBlockIndex = pindex;
     166             :     }
     167             : 
     168         835 :     scheduler->scheduleFromNow([&]() {
     169         835 :         EnforceBestChainLock();
     170             :     },
     171             :         0);
     172             : 
     173        1670 :     LogPrint(BCLog::LLMQ, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
     174             :         __func__, clsig.ToString(), from);
     175             : }
     176             : 
     177       41186 : void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* pindexNew)
     178             : {
     179      123558 :     LOCK2(cs_main, cs);
     180             : 
     181       41186 :     if (pindexNew->GetBlockHash() == bestChainLock.blockHash) {
     182           0 :         LogPrintf("CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());
     183             : 
     184           0 :         if (bestChainLock.nHeight != pindexNew->nHeight) {
     185             :             // Should not happen, same as the conflict check from ProcessNewChainLock.
     186           0 :             LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
     187           0 :                 __func__, bestChainLock.ToString(), pindexNew->nHeight);
     188           0 :             return;
     189             :         }
     190             : 
     191             :         // when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the
     192             :         // CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual
     193             :         // block processing logic will handle this when the block arrives
     194           0 :         bestChainLockWithKnownBlock = bestChainLock;
     195           0 :         bestChainLockBlockIndex = pindexNew;
     196             :     }
     197             : }
     198             : 
     199       41471 : void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew)
     200             : {
     201             :     // don't call TrySignChainTip directly but instead let the scheduler call it. This way we ensure that cs_main is
     202             :     // never locked and TrySignChainTip is not called twice in parallel. Also avoids recursive calls due to
     203             :     // EnforceBestChainLock switching chains.
     204       82835 :     LOCK(cs);
     205       41471 :     if (tryLockChainTipScheduled) {
     206         214 :         return;
     207             :     }
     208       41364 :     tryLockChainTipScheduled = true;
     209       82728 :     scheduler->scheduleFromNow([&]() {
     210       41357 :         EnforceBestChainLock();
     211       41357 :         TrySignChainTip();
     212       41357 :         LOCK(cs);
     213       41357 :         tryLockChainTipScheduled = false;
     214       41357 :     },
     215             :         0);
     216             : }
     217             : 
     218       44882 : void CChainLocksHandler::TrySignChainTip()
     219             : {
     220       44882 :     Cleanup();
     221             : 
     222       44882 :     if (!fMasterNode) {
     223       41625 :         return;
     224             :     }
     225             : 
     226        5979 :     if (!g_tiertwo_sync_state.IsBlockchainSynced()) {
     227             :         return;
     228             :     }
     229             : 
     230        5979 :     const CBlockIndex* pindex;
     231        5979 :     {
     232        5979 :         LOCK(cs_main);
     233       11958 :         pindex = chainActive.Tip();
     234             :     }
     235             : 
     236        5979 :     if (!pindex->pprev) {
     237             :         return;
     238             :     }
     239        5979 :     if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
     240             :         return;
     241             :     }
     242             : 
     243             :     // DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
     244             :     // To simplify the initial implementation, we skip this process and directly try to create a CLSIG
     245             :     // This will fail when multiple blocks compete, but we accept this for the initial implementation.
     246             :     // Later, we'll add the multiple attempts process.
     247             : 
     248        4142 :     {
     249        4142 :         LOCK(cs);
     250             : 
     251        4142 :         if (pindex->nHeight == lastSignedHeight) {
     252             :             // already signed this one
     253         885 :             return;
     254             :         }
     255             : 
     256        3257 :         if (bestChainLock.nHeight >= pindex->nHeight) {
     257             :             // already got the same CLSIG or a better one
     258             :             return;
     259             :         }
     260             : 
     261        3257 :         if (InternalHasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
     262             :             // don't sign if another conflicting CLSIG is already present. EnforceBestChainLock will later enforce
     263             :             // the correct chain.
     264             :             return;
     265             :         }
     266             :     }
     267             : 
     268        6514 :     LogPrint(BCLog::LLMQ, "CChainLocksHandler::%s -- trying to sign %s, height=%d\n", __func__, pindex->GetBlockHash().ToString(), pindex->nHeight);
     269             : 
     270        3257 :     uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
     271        3257 :     uint256 msgHash = pindex->GetBlockHash();
     272             : 
     273        3257 :     {
     274        3257 :         LOCK(cs);
     275        3257 :         if (bestChainLock.nHeight >= pindex->nHeight) {
     276             :             // might have happened while we didn't hold cs
     277           0 :             return;
     278             :         }
     279        3257 :         lastSignedHeight = pindex->nHeight;
     280        3257 :         lastSignedRequestId = requestId;
     281        3257 :         lastSignedMsgHash = msgHash;
     282             :     }
     283             : 
     284        3257 :     quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqTypeChainLocks, requestId, msgHash);
     285             : }
     286             : 
     287             : // WARNING: cs_main and cs should not be held!
     288             : // This should also not be called from validation signals, as this might result in recursive calls
     289       45717 : void CChainLocksHandler::EnforceBestChainLock()
     290             : {
     291       45717 :     AssertLockNotHeld(cs);
     292       45717 :     AssertLockNotHeld(cs_main);
     293             : 
     294       45717 :     CChainLockSig clsig;
     295       45717 :     const CBlockIndex* pindex;
     296       45717 :     const CBlockIndex* currentBestChainLockBlockIndex;
     297       45717 :     {
     298       45717 :         LOCK(cs);
     299       45717 :         clsig = bestChainLockWithKnownBlock;
     300       45717 :         pindex = currentBestChainLockBlockIndex = this->bestChainLockBlockIndex;
     301             : 
     302       45717 :         if (!currentBestChainLockBlockIndex) {
     303             :             // we don't have the header/block, so we can't do anything right now
     304       81748 :             return;
     305             :         }
     306             :     }
     307             : 
     308        4843 :     bool activateNeeded;
     309        4843 :     {
     310        4843 :         LOCK(cs_main);
     311             : 
     312             :         // Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
     313             :         // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
     314             :         // and invalidate each of them.
     315        9694 :         while (pindex && !chainActive.Contains(pindex)) {
     316             :             // Invalidate all blocks that have the same prevBlockHash but are not equal to blockHash
     317           4 :             auto itp = mapPrevBlockIndex.equal_range(pindex->pprev->GetBlockHash());
     318          10 :             for (auto jt = itp.first; jt != itp.second; ++jt) {
     319           6 :                 if (jt->second == pindex) {
     320           4 :                     continue;
     321             :                 }
     322           4 :                 LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) invalidates block %s\n",
     323           4 :                     __func__, clsig.ToString(), jt->second->GetBlockHash().ToString());
     324           2 :                 DoInvalidateBlock(jt->second, false);
     325             :             }
     326             : 
     327           4 :             pindex = pindex->pprev;
     328             :         }
     329             :         // In case blocks from the correct chain are invalid at the moment, reconsider them. The only case where this
     330             :         // can happen right now is when missing superblock triggers caused the main chain to be dismissed first. When
     331             :         // the trigger later appears, this should bring us to the correct chain eventually. Please note that this does
     332             :         // NOT enforce invalid blocks in any way, it just causes re-validation.
     333        4843 :         if (!currentBestChainLockBlockIndex->IsValid()) {
     334           0 :             CValidationState state;
     335           0 :             ReconsiderBlock(state, mapBlockIndex.at(currentBestChainLockBlockIndex->GetBlockHash()));
     336             :         }
     337             : 
     338        9686 :         activateNeeded = chainActive.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != currentBestChainLockBlockIndex;
     339             :     }
     340             : 
     341        9686 :     CValidationState state;
     342        4845 :     if (activateNeeded && !ActivateBestChain(state)) {
     343           0 :         LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, state.GetRejectReason());
     344             :     }
     345             : }
     346             : 
     347        2110 : void CChainLocksHandler::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
     348             : {
     349        2110 :     if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
     350        1993 :         return;
     351             :     }
     352             : 
     353        2110 :     CChainLockSig clsig;
     354        2110 :     {
     355        2110 :         LOCK(cs);
     356             : 
     357        2110 :         if (recoveredSig.id != lastSignedRequestId || recoveredSig.msgHash != lastSignedMsgHash) {
     358             :             // this is not what we signed, so lets not create a CLSIG for it
     359        1993 :             return;
     360             :         }
     361         540 :         if (bestChainLock.nHeight >= lastSignedHeight) {
     362             :             // already got the same or a better CLSIG through the CLSIG message
     363             :             return;
     364             :         }
     365             : 
     366         117 :         clsig.nHeight = lastSignedHeight;
     367         117 :         clsig.blockHash = lastSignedMsgHash;
     368         117 :         clsig.sig = recoveredSig.sig.Get();
     369             :     }
     370         117 :     ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
     371             : }
     372             : 
     373             : // WARNING, do not hold cs while calling this method as we'll otherwise run into a deadlock
     374           2 : void CChainLocksHandler::DoInvalidateBlock(const CBlockIndex* pindex, bool activateBestChain)
     375             : {
     376           2 :     auto& params = Params();
     377             : 
     378           2 :     {
     379           2 :         LOCK(cs_main);
     380             : 
     381             :         // get the non-const pointer
     382           2 :         CBlockIndex* pindex2 = mapBlockIndex[pindex->GetBlockHash()];
     383             : 
     384           4 :         CValidationState state;
     385           2 :         if (!InvalidateBlock(state, params, pindex2)) {
     386           0 :             LogPrintf("CChainLocksHandler::%s -- InvalidateBlock failed: %s\n", __func__, state.GetRejectReason());
     387             :             // This should not have happened and we are in a state were it's not safe to continue anymore
     388           0 :             assert(false);
     389             :         }
     390             :     }
     391             : 
     392           4 :     CValidationState state;
     393           2 :     if (activateBestChain && !ActivateBestChain(state)) {
     394           0 :         LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, state.GetRejectReason());
     395             :         // This should not have happened and we are in a state were it's not safe to continue anymore
     396           0 :         assert(false);
     397             :     }
     398           2 : }
     399             : 
     400        1246 : bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash)
     401             : {
     402        1246 :     if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
     403             :         return false;
     404             :     }
     405             : 
     406        1573 :     LOCK(cs);
     407         327 :     return InternalHasChainLock(nHeight, blockHash);
     408             : }
     409             : 
     410         327 : bool CChainLocksHandler::InternalHasChainLock(int nHeight, const uint256& blockHash)
     411             : {
     412         327 :     AssertLockHeld(cs);
     413             : 
     414         327 :     if (!bestChainLockBlockIndex) {
     415             :         return false;
     416             :     }
     417             : 
     418         261 :     if (nHeight > bestChainLockBlockIndex->nHeight) {
     419             :         return false;
     420             :     }
     421             : 
     422         237 :     if (nHeight == bestChainLockBlockIndex->nHeight) {
     423          36 :         return blockHash == bestChainLockBlockIndex->GetBlockHash();
     424             :     }
     425             : 
     426         201 :     auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
     427         201 :     return pAncestor && pAncestor->GetBlockHash() == blockHash;
     428             : }
     429             : 
     430       97639 : bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash)
     431             : {
     432       97639 :     if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
     433             :         return false;
     434             :     }
     435             : 
     436      120437 :     LOCK(cs);
     437       22798 :     return InternalHasConflictingChainLock(nHeight, blockHash);
     438             : }
     439             : 
     440       26890 : bool CChainLocksHandler::InternalHasConflictingChainLock(int nHeight, const uint256& blockHash)
     441             : {
     442       26890 :     AssertLockHeld(cs);
     443             : 
     444       26890 :     if (!bestChainLockBlockIndex) {
     445             :         return false;
     446             :     }
     447             : 
     448       10849 :     if (nHeight > bestChainLockBlockIndex->nHeight) {
     449             :         return false;
     450             :     }
     451             : 
     452          17 :     if (nHeight == bestChainLockBlockIndex->nHeight) {
     453          15 :         return blockHash != bestChainLockBlockIndex->GetBlockHash();
     454             :     }
     455             : 
     456           2 :     auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
     457           2 :     assert(pAncestor);
     458           2 :     return pAncestor->GetBlockHash() != blockHash;
     459             : }
     460             : 
     461       44882 : void CChainLocksHandler::Cleanup()
     462             : {
     463       44882 :     if (!g_tiertwo_sync_state.IsBlockchainSynced()) {
     464       44234 :         return;
     465             :     }
     466             : 
     467       30556 :     {
     468       30556 :         LOCK(cs);
     469       30556 :         if (GetTimeMillis() - lastCleanupTime < CLEANUP_INTERVAL) {
     470       59816 :             return;
     471             :         }
     472             :     }
     473             : 
     474        1944 :     LOCK2(cs_main, cs);
     475             : 
     476         648 :     for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
     477        1134 :         if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) {
     478           0 :             it = seenChainLocks.erase(it);
     479             :         } else {
     480        2916 :             ++it;
     481             :         }
     482             :     }
     483             : 
     484         648 :     lastCleanupTime = GetTimeMillis();
     485             : }
     486             : 
     487             : }

Generated by: LCOV version 1.14