LCOV - code coverage report
Current view: top level - src/evo - smldiff.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 107 127 84.3 %
Date: 2026-06-25 07:23:43 Functions: 11 11 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2017-2025 The Dash Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <evo/smldiff.h>
       6             : 
       7             : #include <evo/deterministicmns.h>
       8             : #include <evo/specialtx.h>
       9             : #include <llmq/blockprocessor.h>
      10             : #include <llmq/commitment.h>
      11             : #include <llmq/quorumsman.h>
      12             : #include <util/std23.h>
      13             : 
      14             : #include <chainparams.h>
      15             : #include <consensus/merkle.h>
      16             : #include <core_io.h>
      17             : #include <deploymentstatus.h>
      18             : #include <node/blockstorage.h>
      19             : #include <serialize.h>
      20             : #include <univalue.h>
      21             : #include <validation.h>
      22             : 
      23             : using node::ReadBlockFromDisk;
      24             : 
      25             : // Forward declaration
      26             : std::optional<std::pair<CBLSSignature, uint32_t>> GetNonNullCoinbaseChainlock(const CBlockIndex* pindex);
      27             : 
      28         940 : CSimplifiedMNListDiff::CSimplifiedMNListDiff() = default;
      29             : 
      30        1016 : CSimplifiedMNListDiff::~CSimplifiedMNListDiff() = default;
      31             : 
      32         134 : bool CSimplifiedMNListDiff::BuildQuorumsDiff(const CBlockIndex* baseBlockIndex, const CBlockIndex* blockIndex,
      33             :                                              const llmq::CQuorumBlockProcessor& quorum_block_processor)
      34             : {
      35         134 :     auto baseQuorums = quorum_block_processor.GetMinedAndActiveCommitmentsUntilBlock(baseBlockIndex);
      36         134 :     auto quorums = quorum_block_processor.GetMinedAndActiveCommitmentsUntilBlock(blockIndex);
      37             : 
      38         134 :     std::set<std::pair<Consensus::LLMQType, uint256>> baseQuorumHashes;
      39         134 :     std::set<std::pair<Consensus::LLMQType, uint256>> quorumHashes;
      40        1012 :     for (const auto& [llmqType, vecBlockIndex] : baseQuorums) {
      41         878 :         for (const auto& blockindex : vecBlockIndex) {
      42         208 :             baseQuorumHashes.emplace(llmqType, blockindex->GetBlockHash());
      43             :         }
      44             :     }
      45        1072 :     for (const auto& [llmqType, vecBlockIndex] : quorums) {
      46         938 :         for (const auto& blockindex : vecBlockIndex) {
      47         268 :             quorumHashes.emplace(llmqType, blockindex->GetBlockHash());
      48             :         }
      49             :     }
      50             : 
      51         342 :     for (const auto& p : baseQuorumHashes) {
      52         208 :         if (!quorumHashes.count(p)) {
      53          58 :             deletedQuorums.emplace_back((uint8_t)p.first, p.second);
      54          58 :         }
      55             :     }
      56         402 :     for (const auto& p : quorumHashes) {
      57         504 :         const auto& [llmqType, hash] = p;
      58         268 :         if (!baseQuorumHashes.count(p)) {
      59         472 :             auto [qc, minedBlockHash] = quorum_block_processor.GetMinedCommitment(llmqType, hash);
      60         118 :             if (minedBlockHash == uint256::ZERO) {
      61           0 :                 return false;
      62             :             }
      63         236 :             newQuorums.emplace_back(std::move(qc));
      64         118 :         }
      65             :     }
      66             : 
      67         134 :     return true;
      68         134 : }
      69             : 
      70          58 : bool CSimplifiedMNListDiff::BuildQuorumChainlockInfo(const llmq::CQuorumManager& qman, const CBlockIndex* blockIndex)
      71             : {
      72             :     // Group quorums (indexes corresponding to entries of newQuorums) per CBlockIndex containing the expected CL
      73             :     // signature in CbTx. We want to avoid to load CbTx now, as more than one quorum will target the same block: hence
      74             :     // we want to load CbTxs once per block (heavy operation).
      75          58 :     std::multimap<const CBlockIndex*, uint16_t> workBaseBlockIndexMap;
      76             : 
      77         194 :     for (const auto [idx, e] : std23::views::enumerate(newQuorums)) {
      78             :         // We assume that we have on hand, quorums that correspond to the hashes queried.
      79             :         // If we cannot find them, something must have gone wrong and we should cease trying
      80             :         // to build any further.
      81         204 :         auto quorum = qman.GetQuorum(e.llmqType, e.quorumHash);
      82          68 :         if (!quorum) {
      83           0 :             LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, quorumHash=%s\n", __func__,
      84             :                       std23::to_underlying(e.llmqType), e.quorumHash.ToString());
      85           0 :             return false;
      86             :         }
      87             : 
      88             :         // In case of rotation, all rotated quorums rely on the CL sig expected in the cycleBlock (the block of the
      89             :         // first DKG) - 8 In case of non-rotation, quorums rely on the CL sig expected in the block of the DKG - 8
      90         204 :         const CBlockIndex* pWorkBaseBlockIndex = blockIndex->GetAncestor(quorum->m_quorum_base_block_index->nHeight -
      91         136 :                                                                          quorum->qc->quorumIndex - 8);
      92             : 
      93          68 :         workBaseBlockIndexMap.insert(std::make_pair(pWorkBaseBlockIndex, idx));
      94          68 :     }
      95             : 
      96          90 :     for (auto it = workBaseBlockIndexMap.begin(); it != workBaseBlockIndexMap.end();) {
      97             :         // Process each key (CBlockIndex containing the expected CL signature in CbTx) of the std::multimap once
      98          32 :         const CBlockIndex* pWorkBaseBlockIndex = it->first;
      99          32 :         const auto cbcl = GetNonNullCoinbaseChainlock(pWorkBaseBlockIndex);
     100          32 :         CBLSSignature sig;
     101          32 :         if (cbcl.has_value()) {
     102          18 :             sig = cbcl.value().first;
     103          18 :         }
     104             :         // Get the range of indexes (values) for the current key and merge them into a single std::set
     105          64 :         const auto [it_begin, it_end] = workBaseBlockIndexMap.equal_range(it->first);
     106          32 :         std::set<uint16_t> idx_set;
     107          64 :         std::transform(it_begin, it_end, std::inserter(idx_set, idx_set.end()),
     108          68 :                        [](const auto& pair) { return pair.second; });
     109             :         // Advance the iterator to the next key
     110          32 :         it = it_end;
     111             : 
     112             :         // Different CBlockIndex can contain the same CL sig in CbTx (both non-null or null during the first blocks after v20 activation)
     113             :         // Hence, we need to merge the std::set if another std::set already exists for the same sig.
     114          32 :         if (auto [it_sig, inserted] = quorumsCLSigs.insert({sig, idx_set}); !inserted) {
     115           6 :             it_sig->second.insert(idx_set.begin(), idx_set.end());
     116           6 :         }
     117          32 :     }
     118             : 
     119          58 :     return true;
     120          58 : }
     121             : 
     122         134 : CSimplifiedMNListDiff BuildSimplifiedDiff(const CDeterministicMNList& from, const CDeterministicMNList& to, bool extended)
     123             : {
     124         134 :     CSimplifiedMNListDiff diffRet;
     125         134 :     diffRet.baseBlockHash = from.GetBlockHash();
     126         134 :     diffRet.blockHash = to.GetBlockHash();
     127             : 
     128         758 :     to.ForEachMN(/*onlyValid=*/false, [&](const auto& toPtr) {
     129         624 :         auto fromPtr = from.GetMN(toPtr.proTxHash);
     130         624 :         if (fromPtr == nullptr) {
     131         258 :             CSimplifiedMNListEntry sme{toPtr.to_sml_entry()};
     132         258 :             diffRet.mnList.push_back(std::move(sme));
     133         258 :         } else {
     134         366 :             CSimplifiedMNListEntry sme1{toPtr.to_sml_entry()};
     135         366 :             CSimplifiedMNListEntry sme2(fromPtr->to_sml_entry());
     136         366 :             if ((sme1 != sme2) || (extended && (sme1.scriptPayout != sme2.scriptPayout ||
     137           0 :                                                 sme1.scriptOperatorPayout != sme2.scriptOperatorPayout))) {
     138           0 :                 diffRet.mnList.push_back(std::move(sme1));
     139           0 :             }
     140         366 :         }
     141         624 :     });
     142             : 
     143         504 :     from.ForEachMN(/*onlyValid=*/false, [&](const auto& fromPtr) {
     144         370 :         auto toPtr = to.GetMN(fromPtr.proTxHash);
     145         370 :         if (toPtr == nullptr) {
     146           4 :             diffRet.deletedMNs.emplace_back(fromPtr.proTxHash);
     147           4 :         }
     148         370 :     });
     149             : 
     150         134 :     return diffRet;
     151         134 : }
     152             : 
     153         134 : bool BuildSimplifiedMNListDiff(CDeterministicMNManager& dmnman, const ChainstateManager& chainman,
     154             :                                const llmq::CQuorumBlockProcessor& qblockman, const llmq::CQuorumManager& qman,
     155             :                                const uint256& baseBlockHash, const uint256& blockHash,
     156             :                                CSimplifiedMNListDiff& mnListDiffRet, std::string& errorRet, bool extended)
     157             : {
     158         134 :     AssertLockHeld(::cs_main);
     159         134 :     mnListDiffRet = CSimplifiedMNListDiff();
     160             : 
     161         134 :     const CBlockIndex* baseBlockIndex = chainman.ActiveChain().Genesis();
     162         134 :     if (!baseBlockHash.IsNull()) {
     163          94 :         baseBlockIndex = chainman.m_blockman.LookupBlockIndex(baseBlockHash);
     164          94 :         if (!baseBlockIndex) {
     165           0 :             errorRet = strprintf("block %s not found", baseBlockHash.ToString());
     166           0 :             return false;
     167             :         }
     168          94 :     }
     169             : 
     170         134 :     const CBlockIndex* blockIndex = chainman.m_blockman.LookupBlockIndex(blockHash);
     171         134 :     if (!blockIndex) {
     172           0 :         errorRet = strprintf("block %s not found", blockHash.ToString());
     173           0 :         return false;
     174             :     }
     175             : 
     176         134 :     if (!chainman.ActiveChain().Contains(baseBlockIndex) || !chainman.ActiveChain().Contains(blockIndex)) {
     177           0 :         errorRet = strprintf("block %s and %s are not in the same chain", baseBlockHash.ToString(), blockHash.ToString());
     178           0 :         return false;
     179             :     }
     180         134 :     if (baseBlockIndex->nHeight > blockIndex->nHeight) {
     181           0 :         errorRet = strprintf("base block %s is higher then block %s", baseBlockHash.ToString(), blockHash.ToString());
     182           0 :         return false;
     183             :     }
     184             : 
     185         134 :     auto baseDmnList = dmnman.GetListForBlock(baseBlockIndex);
     186         134 :     auto dmnList = dmnman.GetListForBlock(blockIndex);
     187         134 :     mnListDiffRet = BuildSimplifiedDiff(baseDmnList, dmnList, extended);
     188             : 
     189             :     // We need to return the value that was provided by the other peer as it otherwise won't be able to recognize the
     190             :     // response. This will usually be identical to the block found in baseBlockIndex. The only difference is when a
     191             :     // null block hash was provided to get the diff from the genesis block.
     192         134 :     mnListDiffRet.baseBlockHash = baseBlockHash;
     193             : 
     194         134 :     if (!mnListDiffRet.BuildQuorumsDiff(baseBlockIndex, blockIndex, qblockman)) {
     195           0 :         errorRet = strprintf("failed to build quorums diff");
     196           0 :         return false;
     197             :     }
     198             : 
     199         134 :     if (DeploymentActiveAfter(blockIndex, chainman.GetConsensus(), Consensus::DEPLOYMENT_V20)) {
     200          58 :         if (!mnListDiffRet.BuildQuorumChainlockInfo(qman, blockIndex)) {
     201           0 :             errorRet = strprintf("failed to build quorum chainlock info");
     202           0 :             return false;
     203             :         }
     204          58 :     }
     205             : 
     206             :     // TODO store coinbase TX in CBlockIndex
     207         134 :     CBlock block;
     208         134 :     if (!ReadBlockFromDisk(block, blockIndex, chainman.GetConsensus())) {
     209           0 :         errorRet = strprintf("failed to read block %s from disk", blockHash.ToString());
     210           0 :         return false;
     211             :     }
     212             : 
     213         134 :     mnListDiffRet.cbTx = CMutableTransaction(*block.vtx[0]);
     214             : 
     215         134 :     std::vector<uint256> vHashes;
     216         134 :     std::vector<bool> vMatch(block.vtx.size(), false);
     217         440 :     for (const auto& tx : block.vtx) {
     218         306 :         vHashes.emplace_back(tx->GetHash());
     219             :     }
     220         134 :     vMatch[0] = true; // only coinbase matches
     221         134 :     mnListDiffRet.cbTxMerkleTree = CPartialMerkleTree(vHashes, vMatch);
     222             : 
     223         134 :     return true;
     224         134 : }

Generated by: LCOV version 1.16