LCOV - code coverage report
Current view: top level - src/llmq - commitment.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 137 180 76.1 %
Date: 2026-06-25 07:23:43 Functions: 9 9 100.0 %

          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      119896 : 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       59948 : {
      33       59948 : }
      34             : 
      35        9310 : bool CFinalCommitment::VerifySignatureAsync(const llmq::UtilParameters& util_params,
      36             :                                             CCheckQueueControl<utils::BlsCheck>* queue_control) const
      37             : {
      38        9310 :     auto members = utils::GetAllQuorumMembers(llmqType, util_params);
      39        9310 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
      40        9310 :     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        9310 :     const auto& llmq_params = llmq_params_opt.value();
      46             : 
      47       18620 :     uint256 commitmentHash = BuildCommitmentHash(llmq_params.type, quorumHash, validMembers, quorumPublicKey,
      48        9310 :                                                  quorumVvecHash);
      49        9310 :     if (LogAcceptDebug(BCLog::LLMQ)) {
      50        9310 :         std::stringstream ss3;
      51       43431 :         for (const auto& mn : members) {
      52       34121 :             ss3 << mn->proTxHash.ToString().substr(0, 4) << " | ";
      53             :         }
      54        9310 :         LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", __func__,
      55             :                  ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString());
      56        9310 :     }
      57        9310 :     if (llmq_params.is_single_member()) {
      58         215 :         LogPrintf("pubkey operator: %s\n", members[0]->pdmnState->pubKeyOperator.Get().ToString());
      59         215 :         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         215 :     } else {
      64        9095 :         std::vector<CBLSPublicKey> memberPubKeys;
      65       43001 :         for (const auto i : util::irange(members.size())) {
      66       33906 :             if (!signers[i]) {
      67         449 :                 continue;
      68             :             }
      69       33457 :             memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
      70             :         }
      71        9095 :         std::string members_id_string{
      72        9095 :             strprintf("CFinalCommitment -- q[%s] invalid aggregated members signature", quorumHash.ToString())};
      73        9095 :         if (queue_control) {
      74        4805 :             std::vector<utils::BlsCheck> vChecks;
      75        4805 :             vChecks.emplace_back(membersSig, memberPubKeys, commitmentHash, members_id_string);
      76        4805 :             queue_control->Add(vChecks);
      77        4805 :         } else {
      78        4290 :             if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) {
      79           0 :                 LogPrint(BCLog::LLMQ, "%s\n", members_id_string);
      80           0 :                 return false;
      81             :             }
      82             :         }
      83        9095 :     }
      84        9310 :     std::string qsig_id_string{strprintf("CFinalCommitment -- q[%s] invalid quorum signature", quorumHash.ToString())};
      85        9310 :     if (queue_control) {
      86        4921 :         std::vector<utils::BlsCheck> vChecks;
      87        4921 :         std::vector<CBLSPublicKey> public_keys;
      88        4921 :         public_keys.push_back(quorumPublicKey);
      89        4921 :         vChecks.emplace_back(quorumSig, public_keys, commitmentHash, qsig_id_string);
      90        4921 :         queue_control->Add(vChecks);
      91        4921 :     } else {
      92        4389 :         if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) {
      93           0 :             LogPrint(BCLog::LLMQ, "%s\n", qsig_id_string);
      94           0 :             return false;
      95             :         }
      96             :     }
      97        9310 :     return true;
      98        9310 : }
      99             : 
     100             : 
     101       14231 : bool CFinalCommitment::Verify(const llmq::UtilParameters& util_params, bool checkSigs) const
     102             : {
     103       14231 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     104       14231 :     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       14231 :     const auto& llmq_params = llmq_params_opt.value();
     109             : 
     110       14231 :     const uint16_t expected_nversion{
     111       28462 :         CFinalCommitment::GetVersion(IsQuorumRotationEnabled(llmq_params, util_params.m_base_index),
     112       14231 :                                      DeploymentActiveAfter(util_params.m_base_index, util_params.m_chainman.GetConsensus(),
     113             :                                                            Consensus::DEPLOYMENT_V19))};
     114       14231 :     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       14231 :     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       14231 :     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       14231 :     if (!VerifySizes(llmq_params)) {
     130           0 :         return false;
     131             :     }
     132             : 
     133       14231 :     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       14231 :     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       14231 :     if (!quorumPublicKey.IsValid()) {
     142           2 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumPublicKey\n", quorumHash.ToString());
     143           2 :         return false;
     144             :     }
     145       14229 :     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       14229 :     if (!membersSig.IsValid()) {
     150           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid membersSig\n", quorumHash.ToString());
     151           0 :         return false;
     152             :     }
     153       14229 :     if (!quorumSig.IsValid()) {
     154           0 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid vvecSig\n", quorumHash.ToString());
     155           0 :         return false;
     156             :     }
     157       14229 :     auto members = utils::GetAllQuorumMembers(llmqType, util_params);
     158       14229 :     if (LogAcceptDebug(BCLog::LLMQ)) {
     159       14229 :         std::stringstream ss;
     160       14229 :         std::stringstream ss2;
     161       67833 :         for (const auto i : util::irange(llmq_params.size)) {
     162       53604 :             ss << "v[" << i << "]=" << validMembers[i];
     163       53604 :             ss2 << "s[" << i << "]=" << signers[i];
     164             :         }
     165       14229 :         LogPrint(BCLog::LLMQ, "CFinalCommitment::%s mns[%d] validMembers[%s] signers[%s]\n", __func__, members.size(), ss.str(), ss2.str());
     166       14229 :     }
     167             : 
     168       15889 :     for (const auto i : std::views::iota(members.size(), size_t(llmq_params.size))) {
     169        1662 :         if (validMembers[i]) {
     170           2 :             LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers bitset. bit %d should not be set\n", quorumHash.ToString(), i);
     171           2 :             return false;
     172             :         }
     173        1660 :         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       14227 :     if (checkSigs) {
     181        4389 :         if (!VerifySignatureAsync(util_params, /*queue_control=*/nullptr)) {
     182           0 :             return false;
     183             :         }
     184        4389 :     }
     185             : 
     186       14227 :     LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] VALID QUORUM\n", quorumHash.ToString());
     187             : 
     188       14227 :     return true;
     189       14231 : }
     190             : 
     191      456940 : bool CFinalCommitment::VerifyNull() const
     192             : {
     193      456940 :     const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
     194      456940 :     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      456940 :     if (!IsNull() || !VerifySizes(llmq_params_opt.value())) {
     200           0 :         return false;
     201             :     }
     202             : 
     203      456940 :     return true;
     204      456940 : }
     205             : 
     206      471174 : bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const
     207             : {
     208      471174 :     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      471172 :     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      471172 :     return true;
     218      471174 : }
     219             : 
     220      233400 : bool CheckLLMQCommitment(const llmq::UtilParameters& util_params, const CTransaction& tx, TxValidationState& state)
     221             : {
     222      233400 :     const auto opt_qcTx = GetTxPayload<CFinalCommitmentTxPayload>(tx);
     223      233400 :     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      233400 :     auto& qcTx = *opt_qcTx;
     229             : 
     230      233400 :     const auto& llmq_params_opt = Params().GetLLMQ(qcTx.commitment.llmqType);
     231      233400 :     if (!llmq_params_opt.has_value()) {
     232           2 :         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           2 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-commitment-type");
     235             :     }
     236             : 
     237      233398 :     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      233398 :         std::stringstream ss;
     241      233398 :         const auto log_size = std::min<size_t>(llmq_params_opt->size, qcTx.commitment.validMembers.size());
     242     1080487 :         for (const auto i : util::irange(log_size)) {
     243      847089 :             ss << "v[" << i << "]=" << qcTx.commitment.validMembers[i];
     244             :         }
     245      233398 :         LogPrint(BCLog::LLMQ, "CFinalCommitment -- %s llmqType[%d] validMembers[%s] signers[]\n", __func__,
     246             :                                  int(qcTx.commitment.llmqType), ss.str());
     247      233398 :     }
     248             : 
     249      233398 :     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      233398 :     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      233397 :     const CBlockIndex* pQuorumBaseBlockIndex =
     262      466794 :         WITH_LOCK(::cs_main, return util_params.m_chainman.m_blockman.LookupBlockIndex(qcTx.commitment.quorumHash));
     263      233397 :     if (pQuorumBaseBlockIndex == nullptr) {
     264           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash");
     265             :     }
     266             : 
     267      233397 :     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      233397 :     if (qcTx.commitment.IsNull()) {
     273      228472 :         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      228472 :         return true;
     279             :     }
     280             : 
     281        4925 :     if (!qcTx.commitment.Verify(util_params.replace_index(pQuorumBaseBlockIndex), false)) {
     282           4 :         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           4 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid");
     285             :     }
     286             : 
     287        4921 :     LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] CheckLLMQCommitment VALID\n", util_params.m_base_index->nHeight);
     288             : 
     289        4921 :     return true;
     290      233400 : }
     291             : 
     292       31189 : uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHash,
     293             :                                         const std::vector<bool>& validMembers, const CBLSPublicKey& pubKey,
     294             :                                         const uint256& vvecHash)
     295             : {
     296       31189 :     CHashWriter hw(SER_GETHASH, 0);
     297       31189 :     hw << llmqType;
     298       31189 :     hw << blockHash;
     299       31189 :     hw << DYNBITSET(validMembers);
     300       31189 :     hw << pubKey;
     301       31189 :     hw << vvecHash;
     302       31189 :     return hw.GetHash();
     303             : }
     304             : 
     305             : } // namespace llmq

Generated by: LCOV version 1.16