LCOV - code coverage report
Current view: top level - src/llmq - commitment.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 49 180 27.2 %
Date: 2026-06-25 07:23:51 Functions: 7 9 77.8 %

          Line data    Source code
       1             : // Copyright (c) 2018-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <llmq/commitment.h>
       6             : 
       7             : #include <evo/deterministicmns.h>
       8             : #include <evo/specialtx.h>
       9             : #include <llmq/options.h>
      10             : #include <llmq/utils.h>
      11             : #include <util/helpers.h>
      12             : #include <util/std23.h>
      13             : 
      14             : #include <chainparams.h>
      15             : #include <checkqueue.h>
      16             : #include <consensus/validation.h>
      17             : #include <deploymentstatus.h>
      18             : #include <logging.h>
      19             : #include <validation.h>
      20             : 
      21             : #include <algorithm>
      22             : #include <ranges>
      23             : 
      24             : namespace llmq
      25             : {
      26             : 
      27       29172 : CFinalCommitment::CFinalCommitment(const Consensus::LLMQParams& params, const uint256& _quorumHash) :
      28             :         llmqType(params.type),
      29             :         quorumHash(_quorumHash),
      30             :         signers(params.size),
      31             :         validMembers(params.size)
      32       14586 : {
      33       14586 : }
      34             : 
      35           0 : bool CFinalCommitment::VerifySignatureAsync(const llmq::UtilParameters& util_params,
      36             :                                             CCheckQueueControl<utils::BlsCheck>* queue_control) const
      37             : {
      38           0 :     auto members = utils::GetAllQuorumMembers(llmqType, util_params);
      39           0 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
      40           0 :     if (!llmq_params_opt.has_value()) {
      41           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid llmqType=%d\n", quorumHash.ToString(),
      42             :                  std23::to_underlying(llmqType));
      43           0 :         return false;
      44             :     }
      45           0 :     const auto& llmq_params = llmq_params_opt.value();
      46             : 
      47           0 :     uint256 commitmentHash = BuildCommitmentHash(llmq_params.type, quorumHash, validMembers, quorumPublicKey,
      48           0 :                                                  quorumVvecHash);
      49           0 :     if (LogAcceptDebug(BCLog::LLMQ)) {
      50           0 :         std::stringstream ss3;
      51           0 :         for (const auto& mn : members) {
      52           0 :             ss3 << mn->proTxHash.ToString().substr(0, 4) << " | ";
      53             :         }
      54           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", __func__,
      55             :                  ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString());
      56           0 :     }
      57           0 :     if (llmq_params.is_single_member()) {
      58           0 :         LogPrintf("pubkey operator: %s\n", members[0]->pdmnState->pubKeyOperator.Get().ToString());
      59           0 :         if (!membersSig.VerifyInsecure(members[0]->pdmnState->pubKeyOperator.Get(), commitmentHash)) {
      60           0 :             LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid member signature\n", quorumHash.ToString());
      61           0 :             return false;
      62             :         }
      63           0 :     } else {
      64           0 :         std::vector<CBLSPublicKey> memberPubKeys;
      65           0 :         for (const auto i : util::irange(members.size())) {
      66           0 :             if (!signers[i]) {
      67           0 :                 continue;
      68             :             }
      69           0 :             memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
      70             :         }
      71           0 :         std::string members_id_string{
      72           0 :             strprintf("CFinalCommitment -- q[%s] invalid aggregated members signature", quorumHash.ToString())};
      73           0 :         if (queue_control) {
      74           0 :             std::vector<utils::BlsCheck> vChecks;
      75           0 :             vChecks.emplace_back(membersSig, memberPubKeys, commitmentHash, members_id_string);
      76           0 :             queue_control->Add(vChecks);
      77           0 :         } else {
      78           0 :             if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) {
      79           0 :                 LogPrint(BCLog::LLMQ, "%s\n", members_id_string);
      80           0 :                 return false;
      81             :             }
      82             :         }
      83           0 :     }
      84           0 :     std::string qsig_id_string{strprintf("CFinalCommitment -- q[%s] invalid quorum signature", quorumHash.ToString())};
      85           0 :     if (queue_control) {
      86           0 :         std::vector<utils::BlsCheck> vChecks;
      87           0 :         std::vector<CBLSPublicKey> public_keys;
      88           0 :         public_keys.push_back(quorumPublicKey);
      89           0 :         vChecks.emplace_back(quorumSig, public_keys, commitmentHash, qsig_id_string);
      90           0 :         queue_control->Add(vChecks);
      91           0 :     } else {
      92           0 :         if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) {
      93           0 :             LogPrint(BCLog::LLMQ, "%s\n", qsig_id_string);
      94           0 :             return false;
      95             :         }
      96             :     }
      97           0 :     return true;
      98           0 : }
      99             : 
     100             : 
     101           0 : bool CFinalCommitment::Verify(const llmq::UtilParameters& util_params, bool checkSigs) const
     102             : {
     103           0 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     104           0 :     if (!llmq_params_opt.has_value()) {
     105           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid llmqType=%d\n", quorumHash.ToString(), std23::to_underlying(llmqType));
     106           0 :         return false;
     107             :     }
     108           0 :     const auto& llmq_params = llmq_params_opt.value();
     109             : 
     110           0 :     const uint16_t expected_nversion{
     111           0 :         CFinalCommitment::GetVersion(IsQuorumRotationEnabled(llmq_params, util_params.m_base_index),
     112           0 :                                      DeploymentActiveAfter(util_params.m_base_index, util_params.m_chainman.GetConsensus(),
     113             :                                                            Consensus::DEPLOYMENT_V19))};
     114           0 :     if (nVersion == 0 || nVersion != expected_nversion) {
     115           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid nVersion=%d expected=%d\n", quorumHash.ToString(), nVersion, expected_nversion);
     116           0 :         return false;
     117             :     }
     118             : 
     119           0 :     if (util_params.m_base_index->GetBlockHash() != quorumHash) {
     120           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumHash\n", quorumHash.ToString());
     121           0 :         return false;
     122             :     }
     123             : 
     124           0 :     if ((util_params.m_base_index->nHeight % llmq_params.dkgInterval) != quorumIndex) {
     125           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumIndex=%d\n", quorumHash.ToString(), quorumIndex);
     126           0 :         return false;
     127             :     }
     128             : 
     129           0 :     if (!VerifySizes(llmq_params)) {
     130           0 :         return false;
     131             :     }
     132             : 
     133           0 :     if (CountValidMembers() < llmq_params.minSize) {
     134           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers count. validMembersCount=%d\n", quorumHash.ToString(), CountValidMembers());
     135           0 :         return false;
     136             :     }
     137           0 :     if (CountSigners() < llmq_params.minSize) {
     138           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid signers count. signersCount=%d\n", quorumHash.ToString(), CountSigners());
     139           0 :         return false;
     140             :     }
     141           0 :     if (!quorumPublicKey.IsValid()) {
     142           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumPublicKey\n", quorumHash.ToString());
     143           0 :         return false;
     144             :     }
     145           0 :     if (!llmq_params.is_single_member() && quorumVvecHash.IsNull()) {
     146           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumVvecHash\n", quorumHash.ToString());
     147           0 :         return false;
     148             :     }
     149           0 :     if (!membersSig.IsValid()) {
     150           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid membersSig\n", quorumHash.ToString());
     151           0 :         return false;
     152             :     }
     153           0 :     if (!quorumSig.IsValid()) {
     154           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid vvecSig\n", quorumHash.ToString());
     155           0 :         return false;
     156             :     }
     157           0 :     auto members = utils::GetAllQuorumMembers(llmqType, util_params);
     158           0 :     if (LogAcceptDebug(BCLog::LLMQ)) {
     159           0 :         std::stringstream ss;
     160           0 :         std::stringstream ss2;
     161           0 :         for (const auto i : util::irange(llmq_params.size)) {
     162           0 :             ss << "v[" << i << "]=" << validMembers[i];
     163           0 :             ss2 << "s[" << i << "]=" << signers[i];
     164             :         }
     165           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment::%s mns[%d] validMembers[%s] signers[%s]\n", __func__, members.size(), ss.str(), ss2.str());
     166           0 :     }
     167             : 
     168           0 :     for (const auto i : std::views::iota(members.size(), size_t(llmq_params.size))) {
     169           0 :         if (validMembers[i]) {
     170           0 :             LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers bitset. bit %d should not be set\n", quorumHash.ToString(), i);
     171           0 :             return false;
     172             :         }
     173           0 :         if (signers[i]) {
     174           0 :             LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid signers bitset. bit %d should not be set\n", quorumHash.ToString(), i);
     175           0 :             return false;
     176             :         }
     177             :     }
     178             : 
     179             :     // sigs are only checked when the block is processed
     180           0 :     if (checkSigs) {
     181           0 :         if (!VerifySignatureAsync(util_params, /*queue_control=*/nullptr)) {
     182           0 :             return false;
     183             :         }
     184           0 :     }
     185             : 
     186           0 :     LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] VALID QUORUM\n", quorumHash.ToString());
     187             : 
     188           0 :     return true;
     189           0 : }
     190             : 
     191       58100 : bool CFinalCommitment::VerifyNull() const
     192             : {
     193       58100 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     194       58100 :     if (!llmq_params_opt.has_value()) {
     195           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s]invalid llmqType=%d\n", quorumHash.ToString(), std23::to_underlying(llmqType));
     196           0 :         return false;
     197             :     }
     198             : 
     199       58100 :     if (!IsNull() || !VerifySizes(llmq_params_opt.value())) {
     200           0 :         return false;
     201             :     }
     202             : 
     203       58100 :     return true;
     204       58100 : }
     205             : 
     206       58103 : bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const
     207             : {
     208       58103 :     if (signers.size() != size_t(params.size)) {
     209           2 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid signers.size=%d\n", quorumHash.ToString(), signers.size());
     210           2 :         return false;
     211             :     }
     212       58101 :     if (validMembers.size() != size_t(params.size)) {
     213           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers.size=%d\n", quorumHash.ToString(),
     214             :                  validMembers.size());
     215           0 :         return false;
     216             :     }
     217       58101 :     return true;
     218       58103 : }
     219             : 
     220       29051 : bool CheckLLMQCommitment(const llmq::UtilParameters& util_params, const CTransaction& tx, TxValidationState& state)
     221             : {
     222       29051 :     const auto opt_qcTx = GetTxPayload<CFinalCommitmentTxPayload>(tx);
     223       29051 :     if (!opt_qcTx) {
     224           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] GetTxPayload LLMQCommitment failed\n",
     225             :                  util_params.m_base_index->nHeight);
     226           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-payload");
     227             :     }
     228       29051 :     auto& qcTx = *opt_qcTx;
     229             : 
     230       29051 :     const auto& llmq_params_opt = Params().GetLLMQ(qcTx.commitment.llmqType);
     231       29051 :     if (!llmq_params_opt.has_value()) {
     232           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] GetLLMQ failed for llmqType[%d]\n",
     233             :                  util_params.m_base_index->nHeight, std23::to_underlying(qcTx.commitment.llmqType));
     234           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-commitment-type");
     235             :     }
     236             : 
     237       29051 :     if (LogAcceptDebug(BCLog::LLMQ)) {
     238             :         // Clamp to validMembers.size() because the wire-format DYNBITSET may be smaller than
     239             :         // llmq_params.size for malformed payloads; VerifySizes() below catches the mismatch.
     240       29051 :         std::stringstream ss;
     241       29051 :         const auto log_size = std::min<size_t>(llmq_params_opt->size, qcTx.commitment.validMembers.size());
     242      129785 :         for (const auto i : util::irange(log_size)) {
     243      100734 :             ss << "v[" << i << "]=" << qcTx.commitment.validMembers[i];
     244             :         }
     245       29051 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- %s llmqType[%d] validMembers[%s] signers[]\n", __func__,
     246             :                                  int(qcTx.commitment.llmqType), ss.str());
     247       29051 :     }
     248             : 
     249       29051 :     if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) {
     250           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.nVersion[%d]\n",
     251             :                  util_params.m_base_index->nHeight, qcTx.nVersion);
     252           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-version");
     253             :     }
     254             : 
     255       29051 :     if (qcTx.nHeight != uint32_t(util_params.m_base_index->nHeight + 1)) {
     256           1 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.nHeight[%d]\n", util_params.m_base_index->nHeight,
     257             :                  qcTx.nHeight);
     258           1 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-height");
     259             :     }
     260             : 
     261       29050 :     const CBlockIndex* pQuorumBaseBlockIndex =
     262       58100 :         WITH_LOCK(::cs_main, return util_params.m_chainman.m_blockman.LookupBlockIndex(qcTx.commitment.quorumHash));
     263       29050 :     if (pQuorumBaseBlockIndex == nullptr) {
     264           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash");
     265             :     }
     266             : 
     267       29050 :     if (pQuorumBaseBlockIndex != util_params.m_base_index->GetAncestor(pQuorumBaseBlockIndex->nHeight)) {
     268             :         // not part of active chain
     269           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash");
     270             :     }
     271             : 
     272       29050 :     if (qcTx.commitment.IsNull()) {
     273       29050 :         if (!qcTx.commitment.VerifyNull()) {
     274           0 :             LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.commitment[%s] VerifyNull failed\n",
     275             :                      util_params.m_base_index->nHeight, qcTx.commitment.quorumHash.ToString());
     276           0 :             return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid-null");
     277             :         }
     278       29050 :         return true;
     279             :     }
     280             : 
     281           0 :     if (!qcTx.commitment.Verify(util_params.replace_index(pQuorumBaseBlockIndex), false)) {
     282           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.commitment[%s] Verify failed\n",
     283             :                  util_params.m_base_index->nHeight, qcTx.commitment.quorumHash.ToString());
     284           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid");
     285             :     }
     286             : 
     287           0 :     LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] CheckLLMQCommitment VALID\n", util_params.m_base_index->nHeight);
     288             : 
     289           0 :     return true;
     290       29051 : }
     291             : 
     292          28 : uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHash,
     293             :                                         const std::vector<bool>& validMembers, const CBLSPublicKey& pubKey,
     294             :                                         const uint256& vvecHash)
     295             : {
     296          28 :     CHashWriter hw(SER_GETHASH, 0);
     297          28 :     hw << llmqType;
     298          28 :     hw << blockHash;
     299          28 :     hw << DYNBITSET(validMembers);
     300          28 :     hw << pubKey;
     301          28 :     hw << vvecHash;
     302          28 :     return hw.GetHash();
     303             : }
     304             : 
     305             : } // namespace llmq

Generated by: LCOV version 1.16