LCOV - code coverage report
Current view: top level - src/llmq - quorums_dkgsession.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 601 739 81.3 %
Date: 2025-04-02 01:23:23 Functions: 33 33 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2018-2021 The Dash Core developers
       2             : // Copyright (c) 2022 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 "llmq/quorums_dkgsession.h"
       7             : 
       8             : #include "activemasternode.h"
       9             : #include "bls/key_io.h"
      10             : #include "chainparams.h"
      11             : #include "cxxtimer.h"
      12             : #include "init.h"
      13             : #include "llmq/quorums_connections.h"
      14             : #include "llmq/quorums_commitment.h"
      15             : #include "llmq/quorums_debug.h"
      16             : #include "llmq/quorums_dkgsessionmgr.h"
      17             : #include "net.h"
      18             : #include "tiertwo/masternode_meta_manager.h"
      19             : #include "univalue.h"
      20             : #include "validation.h"
      21             : 
      22             : namespace llmq
      23             : {
      24             : 
      25             : static RecursiveMutex cs_simDkgError;
      26             : static std::map<std::string, double> simDkgErrorMap = {
      27             :         {"contribution-omit", 0.0},
      28             :         {"contribution-lie", 0.0},
      29             :         {"complain-lie", 0.0},
      30             :         {"justify-lie", 0.0},
      31             :         {"justify-omit", 0.0},
      32             :         {"commit-omit", 0.0},
      33             :         {"commit-lie", 0.0}
      34             : };
      35             : 
      36          59 : bool SetSimulatedDKGErrorRate(const std::string& error_type, double rate)
      37             : {
      38          59 :     LOCK(cs_simDkgError);
      39          59 :     auto it = simDkgErrorMap.find(error_type);
      40          59 :     if (it != simDkgErrorMap.end()) {
      41          58 :         it->second = rate;
      42          58 :         return true;
      43             :     }
      44             :     return false;
      45             : }
      46             : 
      47         431 : static double GetSimulatedErrorRate(const std::string& error_type)
      48             : {
      49         431 :     LOCK(cs_simDkgError);
      50         431 :     auto it = simDkgErrorMap.find(error_type);
      51         431 :     if (it != simDkgErrorMap.end()) {
      52         431 :         return it->second;
      53             :     }
      54             :     return 0;
      55             : }
      56             : 
      57         431 : bool CDKGSession::ShouldSimulateError(const std::string& error_type)
      58             : {
      59         431 :     if (params.type != Consensus::LLMQType::LLMQ_TEST) {
      60             :         // error simulation available only for LLMQ_TEST
      61             :         return false;
      62             :     }
      63         431 :     double rate = GetSimulatedErrorRate(error_type);
      64         431 :     return GetRandBool(rate);
      65             : }
      66             : 
      67        1417 : CDKGLogger::CDKGLogger(const CDKGSession& _quorumDkg, const std::string& _func) :
      68        1417 :     CDKGLogger(_quorumDkg.params.name, _quorumDkg.pindexQuorum->GetBlockHash(), _quorumDkg.pindexQuorum->nHeight, _quorumDkg.AreWeMember(), _func)
      69             : {
      70        1417 : }
      71             : 
      72        1417 : CDKGLogger::CDKGLogger(const std::string& _llmqTypeName, const uint256& _quorumHash, int _height, bool _areWeMember, const std::string& _func) :
      73        1417 :     CBatchedLogger(g_logger, BCLog::DKG, strprintf("QuorumDKG(type=%s, height=%d, member=%d, func=%s)", _llmqTypeName, _height, _areWeMember, _func))
      74             : {
      75        1417 : }
      76             : 
      77             : 
      78          61 : CDKGComplaint::CDKGComplaint(const Consensus::LLMQParams& params) :
      79         183 :     badMembers((size_t)params.size), complainForMembers((size_t)params.size)
      80             : {
      81          61 : }
      82             : 
      83          55 : CDKGPrematureCommitment::CDKGPrematureCommitment(const Consensus::LLMQParams& params) :
      84         330 :     validMembers((size_t)params.size)
      85             : {
      86          55 : }
      87             : 
      88         474 : CDKGMember::CDKGMember(CDeterministicMNCPtr _dmn, size_t _idx) :
      89             :     dmn(_dmn),
      90             :     idx(_idx),
      91         474 :     id(_dmn->proTxHash)
      92             : {
      93             : 
      94         474 : }
      95             : 
      96         169 : bool CDKGSession::Init(const CBlockIndex* _pindexQuorum, const std::vector<CDeterministicMNCPtr>& mns, const uint256& _myProTxHash)
      97             : {
      98         169 :     if (mns.size() < (size_t)params.minSize) {
      99             :         // don't use CDKGLogger until CDKGSession is fully initialized
     100          11 :         LogPrint(BCLog::DKG, "CDKGSession::%s: not enough members (%d < %d), aborting init\n", __func__, mns.size(), params.minSize);
     101          11 :         return false;
     102             :     }
     103             : 
     104         158 :     pindexQuorum = _pindexQuorum;
     105             : 
     106         158 :     members.resize(mns.size());
     107         158 :     memberIds.resize(members.size());
     108         158 :     receivedVvecs.resize(members.size());
     109         158 :     receivedSkContributions.resize(members.size());
     110             : 
     111         632 :     for (size_t i = 0; i < mns.size(); i++) {
     112         474 :         members[i] = std::unique_ptr<CDKGMember>(new CDKGMember(mns[i], i));
     113         474 :         membersMap.emplace(members[i]->dmn->proTxHash, i);
     114         474 :         memberIds[i] = members[i]->id;
     115             :     }
     116             : 
     117         316 :     if (!_myProTxHash.IsNull()) {
     118         449 :         for (size_t i = 0; i < members.size(); i++) {
     119         376 :             auto& m = members[i];
     120         376 :             if (m->dmn->proTxHash == _myProTxHash) {
     121          79 :                 myIdx = i;
     122          79 :                 myProTxHash = _myProTxHash;
     123          79 :                 myId = m->id;
     124          79 :                 break;
     125             :             }
     126             :         }
     127             :     }
     128             : 
     129         316 :     if (myProTxHash.IsNull()) {
     130          79 :         LogPrint(BCLog::DKG, "CDKGSession::%s: initialized as observer. mns=%d\n", __func__, mns.size());
     131             :     } else {
     132          79 :         quorumDKGDebugManager->InitLocalSessionStatus(params.type, pindexQuorum->GetBlockHash(), pindexQuorum->nHeight);
     133          79 :         relayMembers = GetQuorumRelayMembers(mns, myIdx);
     134          79 :         LogPrint(BCLog::DKG, "CDKGSession::%s: initialized as member. mns=%d\n", __func__, mns.size());
     135             :     }
     136             : 
     137             :     return true;
     138             : }
     139             : 
     140         134 : void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages)
     141             : {
     142         203 :     CDKGLogger logger(*this, __func__);
     143             : 
     144         268 :     if (!AreWeMember()) {
     145          65 :         return;
     146             :     }
     147             : 
     148         138 :     cxxtimer::Timer t1(true);
     149          69 :     logger.Batch("generating contributions");
     150          69 :     if (!blsWorker.GenerateContributions(params.threshold, memberIds, vvecContribution, skContributions)) {
     151             :         // this should never happen actually
     152           0 :         logger.Batch("GenerateContributions failed");
     153          65 :         return;
     154             :     }
     155          69 :     logger.Batch("generated contributions. time=%d", t1.count());
     156             : 
     157          69 :     SendContributions(pendingMessages);
     158             : }
     159             : 
     160          69 : void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages)
     161             : {
     162         137 :     CDKGLogger logger(*this, __func__);
     163             : 
     164         138 :     assert(AreWeMember());
     165             : 
     166          69 :     logger.Batch("sending contributions");
     167             : 
     168         138 :     if (ShouldSimulateError("contribution-omit")) {
     169           1 :         logger.Batch("omitting");
     170           2 :         return;
     171             :     }
     172             : 
     173         136 :     CDKGContribution qc;
     174          68 :     qc.llmqType = (uint8_t)params.type;
     175          68 :     qc.quorumHash = pindexQuorum->GetBlockHash();
     176          68 :     qc.proTxHash = myProTxHash;
     177          68 :     qc.vvec = vvecContribution;
     178             : 
     179         136 :     cxxtimer::Timer t1(true);
     180         136 :     qc.contributions = std::make_shared<CBLSIESMultiRecipientObjects<CBLSSecretKey>>();
     181          68 :     qc.contributions->InitEncrypt(members.size());
     182             : 
     183         272 :     for (size_t i = 0; i < members.size(); i++) {
     184         204 :         auto& m = members[i];
     185         408 :         CBLSSecretKey skContrib = skContributions[i];
     186             : 
     187         470 :         if (i != myIdx && ShouldSimulateError("contribution-lie")) {
     188          12 :             logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
     189           6 :             skContrib.MakeNewKey();
     190             :         }
     191             : 
     192         204 :         if (!qc.contributions->Encrypt(i, m->dmn->pdmnState->pubKeyOperator.Get(), skContrib, PROTOCOL_VERSION)) {
     193           0 :             logger.Batch("failed to encrypt contribution for %s", m->dmn->proTxHash.ToString());
     194           0 :             return;
     195             :         }
     196             :     }
     197             : 
     198          68 :     logger.Batch("encrypted contributions. time=%d", t1.count());
     199             : 
     200          68 :     qc.sig = activeMasternodeManager->OperatorKey()->Sign(qc.GetSignHash());
     201             : 
     202          68 :     logger.Flush();
     203             : 
     204          68 :     quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) {
     205          68 :         status.sentContributions = true;
     206          68 :         return true;
     207             :     });
     208             : 
     209          68 :     pendingMessages.PushPendingMessage(-1, qc, MSG_QUORUM_CONTRIB);
     210             : }
     211             : 
     212             : // only performs cheap verifications, but not the signature of the message. this is checked with batched verification
     213         174 : bool CDKGSession::PreVerifyMessage(const CDKGContribution& qc, bool& retBan) const
     214             : {
     215         348 :     CDKGLogger logger(*this, __func__);
     216             : 
     217         174 :     retBan = false;
     218             : 
     219         174 :     if (qc.quorumHash != pindexQuorum->GetBlockHash()) {
     220           0 :         logger.Batch("contribution for wrong quorum, rejecting");
     221           0 :         return false;
     222             :     }
     223             : 
     224         174 :     auto member = GetMember(qc.proTxHash);
     225         174 :     if (!member) {
     226           0 :         logger.Batch("contributor not a member of this quorum, rejecting contribution");
     227           0 :         retBan = true;
     228           0 :         return false;
     229             :     }
     230             : 
     231         174 :     if (qc.contributions->blobs.size() != members.size()) {
     232           0 :         logger.Batch("invalid contributions count");
     233           0 :         retBan = true;
     234           0 :         return false;
     235             :     }
     236         174 :     if (qc.vvec->size() != (size_t)params.threshold) {
     237           0 :         logger.Batch("invalid verification vector length");
     238           0 :         retBan = true;
     239           0 :         return false;
     240             :     }
     241             : 
     242         174 :     if (!blsWorker.VerifyVerificationVector(*qc.vvec)) {
     243           0 :         logger.Batch("invalid verification vector");
     244           0 :         retBan = true;
     245           0 :         return false;
     246             :     }
     247             : 
     248         174 :     if (member->contributions.size() >= 2) {
     249             :         // don't do any further processing if we got more than 1 valid contributions already
     250             :         // this is a DoS protection against members sending multiple contributions with valid signatures to us
     251             :         // we must bail out before any expensive BLS verification happens
     252           0 :         logger.Batch("dropping contribution from %s as we already got %d contributions", member->dmn->proTxHash.ToString(), member->contributions.size());
     253           0 :         return false;
     254             :     }
     255             : 
     256             :     return true;
     257             : }
     258             : 
     259         174 : void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGContribution& qc, bool& retBan)
     260             : {
     261         346 :     CDKGLogger logger(*this, __func__);
     262             : 
     263         174 :     retBan = false;
     264             : 
     265         174 :     auto member = GetMember(qc.proTxHash);
     266             : 
     267         174 :     cxxtimer::Timer t1(true);
     268         348 :     logger.Batch("received contribution from %s", qc.proTxHash.ToString());
     269             : 
     270         174 :     {
     271             :         // relay, no matter if further verification fails
     272             :         // This ensures the whole quorum sees the bad behavior
     273         174 :         LOCK(invCs);
     274             : 
     275         174 :         if (member->contributions.size() >= 2) {
     276             :             // only relay up to 2 contributions, that's enough to let the other members know about his bad behavior
     277           0 :             return;
     278             :         }
     279             : 
     280         174 :         contributions.emplace(hash, qc);
     281         174 :         member->contributions.emplace(hash);
     282             : 
     283         174 :         CInv inv(MSG_QUORUM_CONTRIB, hash);
     284         174 :         RelayInvToParticipants(inv);
     285             : 
     286         174 :         quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) {
     287         174 :             status.receivedContribution = true;
     288         174 :             return true;
     289             :         });
     290             : 
     291         174 :         if (member->contributions.size() > 1) {
     292             :             // don't do any further processing if we got more than 1 contribution. we already relayed it,
     293             :             // so others know about his bad behavior
     294           0 :             MarkBadMember(member);
     295           0 :             logger.Batch("%s did send multiple contributions", member->dmn->proTxHash.ToString());
     296           0 :             return;
     297             :         }
     298             :     }
     299             : 
     300         174 :     receivedVvecs[member->idx] = qc.vvec;
     301             : 
     302         174 :     int receivedCount = 0;
     303         696 :     for (const auto& m : members) {
     304         522 :         if (!m->contributions.empty()) {
     305         334 :             receivedCount++;
     306             :         }
     307             :     }
     308             : 
     309         174 :     logger.Batch("received and relayed contribution. received=%d/%d, time=%d", receivedCount, members.size(), t1.count());
     310             : 
     311         346 :     cxxtimer::Timer t2(true);
     312             : 
     313         348 :     if (!AreWeMember()) {
     314             :         // can't further validate
     315           2 :         return;
     316             :     }
     317             : 
     318         174 :     dkgManager.WriteVerifiedVvecContribution(params.type, pindexQuorum, qc.proTxHash, qc.vvec);
     319             : 
     320         174 :     bool complain = false;
     321         346 :     CBLSSecretKey skContribution;
     322         174 :     if (!qc.contributions->Decrypt(myIdx, *activeMasternodeManager->OperatorKey(), skContribution, PROTOCOL_VERSION)) {
     323           0 :         logger.Batch("contribution from %s could not be decrypted", member->dmn->proTxHash.ToString());
     324           0 :         complain = true;
     325         392 :     } else if (member->idx != myIdx && ShouldSimulateError("complain-lie")) {
     326           4 :         logger.Batch("lying/complaining for %s", member->dmn->proTxHash.ToString());
     327           2 :         complain = true;
     328             :     }
     329             : 
     330           2 :     if (complain) {
     331           2 :         member->weComplain = true;
     332           2 :         quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) {
     333           2 :             status.weComplain = true;
     334           2 :             return true;
     335             :         });
     336           4 :         return;
     337             :     }
     338             : 
     339         172 :     logger.Batch("decrypted our contribution share. time=%d", t2.count());
     340             : 
     341         172 :     bool verifyPending = false;
     342         172 :     receivedSkContributions[member->idx] = skContribution;
     343         172 :     pendingContributionVerifications.emplace_back(member->idx);
     344         172 :     if (pendingContributionVerifications.size() >= 32) {
     345           0 :         verifyPending = true;
     346             :     }
     347             : 
     348           0 :     if (verifyPending) {
     349           0 :         VerifyPendingContributions();
     350             :     }
     351             : }
     352             : 
     353             : // Verifies all pending secret key contributions in one batch
     354             : // This is done by aggregating the verification vectors belonging to the secret key contributions
     355             : // The resulting aggregated vvec is then used to recover a public key share
     356             : // The public key share must match the public key belonging to the aggregated secret key contributions
     357             : // See CBLSWorker::VerifyContributionShares for more details.
     358          61 : void CDKGSession::VerifyPendingContributions()
     359             : {
     360         118 :     CDKGLogger logger(*this, __func__);
     361             : 
     362         118 :     cxxtimer::Timer t1(true);
     363             : 
     364         118 :     std::vector<size_t> pend = std::move(pendingContributionVerifications);
     365          61 :     if (pend.empty()) {
     366           4 :         return;
     367             :     }
     368             : 
     369         114 :     std::vector<size_t> memberIndexes;
     370          57 :     std::vector<BLSVerificationVectorPtr> vvecs;
     371           0 :     BLSSecretKeyVector skContributions;
     372             : 
     373         211 :     for (const auto& idx : pend) {
     374         154 :         auto& m = members[idx];
     375         154 :         if (m->bad || m->weComplain) {
     376           0 :             continue;
     377             :         }
     378         154 :         memberIndexes.emplace_back(idx);
     379         154 :         vvecs.emplace_back(receivedVvecs[idx]);
     380         154 :         skContributions.emplace_back(receivedSkContributions[idx]);
     381             :     }
     382             : 
     383         114 :     auto result = blsWorker.VerifyContributionShares(myId, vvecs, skContributions);
     384          57 :     if (result.size() != memberIndexes.size()) {
     385           0 :         logger.Batch("VerifyContributionShares returned result of size %d but size %d was expected, something is wrong", result.size(), memberIndexes.size());
     386           0 :         return;
     387             :     }
     388             : 
     389         211 :     for (size_t i = 0; i < memberIndexes.size(); i++) {
     390         154 :         if (!result[i]) {
     391           6 :             auto& m = members[memberIndexes[i]];
     392          12 :             logger.Batch("invalid contribution from %s. will complain later", m->dmn->proTxHash.ToString());
     393           6 :             m->weComplain = true;
     394          12 :             quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, m->idx, [&](CDKGDebugMemberStatus& status) {
     395           6 :                 status.weComplain = true;
     396           6 :                 return true;
     397             :             });
     398             :         } else {
     399         148 :             size_t memberIdx = memberIndexes[i];
     400         148 :             dkgManager.WriteVerifiedSkContribution(params.type, pindexQuorum, members[memberIdx]->dmn->proTxHash, skContributions[i]);
     401             :         }
     402             :     }
     403             : 
     404         114 :     logger.Batch("verified %d pending contributions. time=%d", pend.size(), t1.count());
     405             : }
     406             : 
     407         113 : void CDKGSession::VerifyAndComplain(CDKGPendingMessages& pendingMessages)
     408             : {
     409         226 :     if (!AreWeMember()) {
     410          52 :         return;
     411             :     }
     412             : 
     413          61 :     VerifyPendingContributions();
     414             : 
     415         122 :     CDKGLogger logger(*this, __func__);
     416             : 
     417             :     // we check all members if they sent us their contributions
     418             :     // we consider members as bad if they missed to send anything or if they sent multiple
     419             :     // in both cases we won't give him a second chance as he is either down, buggy or an adversary
     420             :     // we assume that such a participant will be marked as bad by the whole network in most cases,
     421             :     // as propagation will ensure that all nodes see the same vvecs/contributions. In case nodes come to
     422             :     // different conclusions, the aggregation phase will handle this (most voted quorum key wins)
     423             : 
     424         122 :     cxxtimer::Timer t1(true);
     425             : 
     426         244 :     for (const auto& m : members) {
     427         183 :         if (m->bad) {
     428           0 :             continue;
     429             :         }
     430         183 :         if (m->contributions.empty()) {
     431          54 :             logger.Batch("%s did not send any contribution", m->dmn->proTxHash.ToString());
     432          27 :             MarkBadMember(m.get());
     433          27 :             continue;
     434             :         }
     435             :     }
     436             : 
     437          61 :     logger.Batch("verified contributions. time=%d", t1.count());
     438          61 :     logger.Flush();
     439             : 
     440          61 :     VerifyConnectionAndMinProtoVersions();
     441             : 
     442          61 :     SendComplaint(pendingMessages);
     443             : }
     444             : 
     445          61 : void CDKGSession::VerifyConnectionAndMinProtoVersions()
     446             : {
     447          61 :     CDKGLogger logger(*this, __func__);
     448             : 
     449         122 :     std::unordered_map<uint256, int, StaticSaltedHasher> protoMap;
     450           0 :     g_connman->ForEachNode([&](const CNode* pnode) {
     451        1186 :         if (pnode->verifiedProRegTxHash.IsNull()) {
     452             :             return;
     453             :         }
     454         181 :         protoMap.emplace(pnode->verifiedProRegTxHash, pnode->nVersion);
     455             :     });
     456             : 
     457         244 :     for (auto& m : members) {
     458         183 :         if (m->dmn->proTxHash == myProTxHash) {
     459          61 :             continue;
     460             :         }
     461             : 
     462         122 :         auto it = protoMap.find(m->dmn->proTxHash);
     463         122 :         if (it == protoMap.end()) {
     464           8 :             m->bad = true;
     465          24 :             logger.Batch("%s is not connected to us", m->dmn->proTxHash.ToString());
     466         114 :         } else if (it != protoMap.end() && it->second < MNAUTH_NODE_VER_VERSION) {
     467           0 :             m->bad = true;
     468           0 :             logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MNAUTH_NODE_VER_VERSION, it->second);
     469             :         }
     470             : 
     471         122 :         auto lastOutbound = g_mmetaman.GetMetaInfo(m->dmn->proTxHash)->GetLastOutboundSuccess();
     472         122 :         if (GetAdjustedTime() - lastOutbound > 60 * 60) {
     473           6 :             m->bad = true;
     474          18 :             logger.Batch("%s no outbound connection since %d seconds", m->dmn->proTxHash.ToString(), GetAdjustedTime() - lastOutbound);
     475             :         }
     476             :     }
     477          61 : }
     478             : 
     479          61 : void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages)
     480             : {
     481          83 :     CDKGLogger logger(*this, __func__);
     482             : 
     483         122 :     assert(AreWeMember());
     484             : 
     485          83 :     CDKGComplaint qc(params);
     486          61 :     qc.llmqType = (uint8_t)params.type;
     487          61 :     qc.quorumHash = pindexQuorum->GetBlockHash();
     488          61 :     qc.proTxHash = myProTxHash;
     489             : 
     490          61 :     int badCount = 0;
     491          61 :     int complaintCount = 0;
     492         244 :     for (size_t i = 0; i < members.size(); i++) {
     493         183 :         auto& m = members[i];
     494         183 :         if (m->bad) {
     495          27 :             qc.badMembers[i] = true;
     496          27 :             badCount++;
     497         156 :         } else if (m->weComplain) {
     498           8 :             qc.complainForMembers[i] = true;
     499           8 :             complaintCount++;
     500             :         }
     501             :     }
     502             : 
     503          61 :     if (badCount == 0 && complaintCount == 0) {
     504          78 :         return;
     505             :     }
     506             : 
     507          22 :     logger.Batch("sending complaint. badCount=%d, complaintCount=%d", badCount, complaintCount);
     508             : 
     509          22 :     qc.sig = activeMasternodeManager->OperatorKey()->Sign(qc.GetSignHash());
     510             : 
     511          22 :     logger.Flush();
     512             : 
     513          22 :     quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) {
     514          22 :         status.sentComplaint = true;
     515          22 :         return true;
     516             :     });
     517             : 
     518          22 :     pendingMessages.PushPendingMessage(-1, qc, MSG_QUORUM_COMPLAINT);
     519             : }
     520             : 
     521             : // only performs cheap verifications, but not the signature of the message. this is checked with batched verification
     522          46 : bool CDKGSession::PreVerifyMessage(const CDKGComplaint& qc, bool& retBan) const
     523             : {
     524          92 :     CDKGLogger logger(*this, __func__);
     525             : 
     526          46 :     retBan = false;
     527             : 
     528          46 :     if (qc.quorumHash != pindexQuorum->GetBlockHash()) {
     529           0 :         logger.Batch("complaint for wrong quorum, rejecting");
     530           0 :         return false;
     531             :     }
     532             : 
     533          46 :     auto member = GetMember(qc.proTxHash);
     534          46 :     if (!member) {
     535           0 :         logger.Batch("complainer not a member of this quorum, rejecting complaint");
     536           0 :         retBan = true;
     537           0 :         return false;
     538             :     }
     539             : 
     540          46 :     if (qc.badMembers.size() != (size_t)params.size) {
     541           0 :         logger.Batch("invalid badMembers bitset size");
     542           0 :         retBan = true;
     543           0 :         return false;
     544             :     }
     545             : 
     546          46 :     if (qc.complainForMembers.size() != (size_t)params.size) {
     547           0 :         logger.Batch("invalid complainForMembers bitset size");
     548           0 :         retBan = true;
     549           0 :         return false;
     550             :     }
     551             : 
     552          46 :     if (member->complaints.size() >= 2) {
     553             :         // don't do any further processing if we got more than 1 valid complaints already
     554             :         // this is a DoS protection against members sending multiple complaints with valid signatures to us
     555             :         // we must bail out before any expensive BLS verification happens
     556           0 :         logger.Batch("dropping complaint from %s as we already got %d complaints",
     557           0 :                       member->dmn->proTxHash.ToString(), member->complaints.size());
     558           0 :         return false;
     559             :     }
     560             : 
     561             :     return true;
     562             : }
     563             : 
     564          46 : void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGComplaint& qc, bool& retBan)
     565             : {
     566          92 :     CDKGLogger logger(*this, __func__);
     567             : 
     568          46 :     retBan = false;
     569             : 
     570          92 :     logger.Batch("received complaint from %s", qc.proTxHash.ToString());
     571             : 
     572          46 :     auto member = GetMember(qc.proTxHash);
     573             : 
     574          46 :     {
     575          46 :         LOCK(invCs);
     576             : 
     577          46 :         if (member->complaints.size() >= 2) {
     578             :             // only relay up to 2 complaints, that's enough to let the other members know about his bad behavior
     579           0 :             return;
     580             :         }
     581             : 
     582          46 :         complaints.emplace(hash, qc);
     583          46 :         member->complaints.emplace(hash);
     584             : 
     585          46 :         CInv inv(MSG_QUORUM_COMPLAINT, hash);
     586          46 :         RelayInvToParticipants(inv);
     587             : 
     588          46 :         quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) {
     589          46 :             status.receivedComplaint = true;
     590          46 :             return true;
     591             :         });
     592             : 
     593          46 :         if (member->complaints.size() > 1) {
     594             :             // don't do any further processing if we got more than 1 complaint. we already relayed it,
     595             :             // so others know about his bad behavior
     596           0 :             MarkBadMember(member);
     597           0 :             logger.Batch("%s did send multiple complaints", member->dmn->proTxHash.ToString());
     598           0 :             return;
     599             :         }
     600             :     }
     601             : 
     602          46 :     int receivedCount = 0;
     603         184 :     for (size_t i = 0; i < members.size(); i++) {
     604         138 :         auto& m = members[i];
     605         138 :         if (qc.badMembers[i]) {
     606         111 :             logger.Batch("%s voted for %s to be bad", member->dmn->proTxHash.ToString(), m->dmn->proTxHash.ToString());
     607          37 :             m->badMemberVotes.emplace(qc.proTxHash);
     608          74 :             if (AreWeMember() && i == myIdx) {
     609          21 :                 logger.Batch("%s voted for us to be bad", member->dmn->proTxHash.ToString());
     610             :             }
     611             :         }
     612         138 :         if (qc.complainForMembers[i]) {
     613          24 :             m->complaintsFromOthers.emplace(qc.proTxHash);
     614          24 :             m->someoneComplain = true;
     615          24 :             quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, m->idx, [&](CDKGDebugMemberStatus& status) {
     616          24 :                 return status.complaintsFromMembers.emplace(member->idx).second;
     617             :             });
     618          48 :             if (AreWeMember() && i == myIdx) {
     619          24 :                 logger.Batch("%s complained about us", member->dmn->proTxHash.ToString());
     620             :             }
     621             :         }
     622         138 :         if (!m->complaints.empty()) {
     623          68 :             receivedCount++;
     624             :         }
     625             :     }
     626             : 
     627          92 :     logger.Batch("received and relayed complaint. received=%d", receivedCount);
     628             : }
     629             : 
     630         108 : void CDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages)
     631             : {
     632         216 :     if (!AreWeMember()) {
     633          51 :         return;
     634             :     }
     635             : 
     636         114 :     CDKGLogger logger(*this, __func__);
     637             : 
     638         114 :     std::set<uint256> justifyFor;
     639             : 
     640         228 :     for (const auto& m : members) {
     641         171 :         if (m->bad) {
     642          20 :             continue;
     643             :         }
     644         151 :         if (m->badMemberVotes.size() >= (size_t)params.dkgBadVotesThreshold) {
     645           0 :             logger.Batch("%s marked as bad as %d other members voted for this", m->dmn->proTxHash.ToString(), m->badMemberVotes.size());
     646           0 :             MarkBadMember(m.get());
     647           0 :             continue;
     648             :         }
     649         151 :         if (m->complaints.empty()) {
     650         114 :             continue;
     651             :         }
     652          37 :         if (m->complaints.size() != 1) {
     653           0 :             logger.Batch("%s sent multiple complaints", m->dmn->proTxHash.ToString());
     654           0 :             MarkBadMember(m.get());
     655           0 :             continue;
     656             :         }
     657             : 
     658          37 :         auto& qc = complaints.at(*m->complaints.begin());
     659          37 :         if (qc.complainForMembers[myIdx]) {
     660         179 :             justifyFor.emplace(qc.proTxHash);
     661             :         }
     662             :     }
     663             : 
     664          57 :     logger.Flush();
     665          57 :     if (!justifyFor.empty()) {
     666           5 :         SendJustification(pendingMessages, justifyFor);
     667             :     }
     668             : }
     669             : 
     670           5 : void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, const std::set<uint256>& forMembers)
     671             : {
     672           9 :     CDKGLogger logger(*this, __func__);
     673             : 
     674          10 :     assert(AreWeMember());
     675             : 
     676           5 :     logger.Batch("sending justification for %d members", forMembers.size());
     677             : 
     678           9 :     CDKGJustification qj;
     679           5 :     qj.llmqType = (uint8_t)params.type;
     680           5 :     qj.quorumHash = pindexQuorum->GetBlockHash();
     681           5 :     qj.proTxHash = myProTxHash;
     682           5 :     qj.contributions.reserve(forMembers.size());
     683             : 
     684          20 :     for (size_t i = 0; i < members.size(); i++) {
     685          15 :         auto& m = members[i];
     686          15 :         if (!forMembers.count(m->dmn->proTxHash)) {
     687           7 :             continue;
     688             :         }
     689          16 :         logger.Batch("justifying for %s", m->dmn->proTxHash.ToString());
     690             : 
     691          16 :         CBLSSecretKey skContribution = skContributions[i];
     692             : 
     693          22 :         if (i != myIdx && ShouldSimulateError("justify-lie")) {
     694           4 :             logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
     695           2 :             skContribution.MakeNewKey();
     696             :         }
     697             : 
     698           8 :         qj.contributions.emplace_back(i, skContribution);
     699             :     }
     700             : 
     701           5 :     if (ShouldSimulateError("justify-omit")) {
     702           1 :         logger.Batch("omitting");
     703           1 :         return;
     704             :     }
     705             : 
     706           4 :     qj.sig = activeMasternodeManager->OperatorKey()->Sign(qj.GetSignHash());
     707             : 
     708           4 :     logger.Flush();
     709             : 
     710           4 :     quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) {
     711           4 :         status.sentJustification = true;
     712           4 :         return true;
     713             :     });
     714             : 
     715           4 :     pendingMessages.PushPendingMessage(-1, qj, MSG_QUORUM_JUSTIFICATION);
     716             : }
     717             : 
     718             : // only performs cheap verifications, but not the signature of the message. this is checked with batched verification
     719          12 : bool CDKGSession::PreVerifyMessage(const CDKGJustification& qj, bool& retBan) const
     720             : {
     721          24 :     CDKGLogger logger(*this, __func__);
     722             : 
     723          12 :     retBan = false;
     724             : 
     725          12 :     if (qj.quorumHash != pindexQuorum->GetBlockHash()) {
     726           0 :         logger.Batch("justification for wrong quorum, rejecting");
     727           0 :         return false;
     728             :     }
     729             : 
     730          12 :     auto member = GetMember(qj.proTxHash);
     731          12 :     if (!member) {
     732           0 :         logger.Batch("justifier not a member of this quorum, rejecting justification");
     733           0 :         retBan = true;
     734           0 :         return false;
     735             :     }
     736             : 
     737          12 :     if (qj.contributions.empty()) {
     738           0 :         logger.Batch("justification with no contributions");
     739           0 :         retBan = true;
     740           0 :         return false;
     741             :     }
     742             : 
     743          24 :     std::set<size_t> contributionsSet;
     744          30 :     for (const auto& p : qj.contributions) {
     745          18 :         if (p.first > members.size()) {
     746           0 :             logger.Batch("invalid contribution index");
     747           0 :             retBan = true;
     748           0 :             return false;
     749             :         }
     750             : 
     751          18 :         if (!contributionsSet.emplace(p.first).second) {
     752           0 :             logger.Batch("duplicate contribution index");
     753           0 :             retBan = true;
     754           0 :             return false;
     755             :         }
     756             : 
     757          18 :         auto& skShare = p.second;
     758          18 :         if (!skShare.IsValid()) {
     759           0 :             logger.Batch("invalid contribution");
     760           0 :             retBan = true;
     761           0 :             return false;
     762             :         }
     763             :     }
     764             : 
     765          12 :     if (member->justifications.size() >= 2) {
     766             :         // don't do any further processing if we got more than 1 valid justification already
     767             :         // this is a DoS protection against members sending multiple justifications with valid signatures to us
     768             :         // we must bail out before any expensive BLS verification happens
     769           0 :         logger.Batch("dropping justification from %s as we already got %d justifications",
     770           0 :                       member->dmn->proTxHash.ToString(), member->justifications.size());
     771           0 :         return false;
     772             :     }
     773             : 
     774             :     return true;
     775             : }
     776             : 
     777          12 : void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGJustification& qj, bool& retBan)
     778             : {
     779          24 :     CDKGLogger logger(*this, __func__);
     780             : 
     781          12 :     retBan = false;
     782             : 
     783          24 :     logger.Batch("received justification from %s", qj.proTxHash.ToString());
     784             : 
     785          12 :     auto member = GetMember(qj.proTxHash);
     786             : 
     787          12 :     {
     788          12 :         LOCK(invCs);
     789             : 
     790          12 :         if (member->justifications.size() >= 2) {
     791             :             // only relay up to 2 justifications, that's enough to let the other members know about his bad behavior
     792           0 :             return;
     793             :         }
     794             : 
     795          12 :         justifications.emplace(hash, qj);
     796          12 :         member->justifications.emplace(hash);
     797             : 
     798             :         // we always relay, even if further verification fails
     799          12 :         CInv inv(MSG_QUORUM_JUSTIFICATION, hash);
     800          12 :         RelayInvToParticipants(inv);
     801             : 
     802          12 :         quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) {
     803          12 :             status.receivedJustification = true;
     804          12 :             return true;
     805             :         });
     806             : 
     807          12 :         if (member->justifications.size() > 1) {
     808             :             // don't do any further processing if we got more than 1 justification. we already relayed it,
     809             :             // so others know about his bad behavior
     810           0 :             logger.Batch("%s did send multiple justifications", member->dmn->proTxHash.ToString());
     811           0 :             MarkBadMember(member);
     812             :             return;
     813             :         }
     814             : 
     815          12 :         if (member->bad) {
     816             :             // we locally determined him to be bad (sent none or more then one contributions)
     817             :             // don't give him a second chance (but we relay the justification in case other members disagree)
     818             :             return;
     819             :         }
     820             :     }
     821             : 
     822          30 :     for (const auto& p : qj.contributions) {
     823          18 :         auto& member2 = members[p.first];
     824             : 
     825          36 :         if (!member->complaintsFromOthers.count(member2->dmn->proTxHash)) {
     826           0 :             logger.Batch("got justification from %s for %s even though he didn't complain",
     827           0 :                             member->dmn->proTxHash.ToString(), member2->dmn->proTxHash.ToString());
     828           0 :             MarkBadMember(member);
     829             :         }
     830             :     }
     831          12 :     if (member->bad) {
     832             :         return;
     833             :     }
     834             : 
     835          24 :     cxxtimer::Timer t1(true);
     836             : 
     837          24 :     std::list<std::future<bool>> futures;
     838          30 :     for (const auto& p : qj.contributions) {
     839          18 :         auto& member2 = members[p.first];
     840          18 :         auto& skContribution = p.second;
     841             : 
     842             :         // watch out to not bail out before these async calls finish (they rely on valid references)
     843          36 :         futures.emplace_back(blsWorker.AsyncVerifyContributionShare(member2->id, receivedVvecs[member->idx], skContribution));
     844             :     }
     845          12 :     auto resultIt = futures.begin();
     846          30 :     for (const auto& p : qj.contributions) {
     847          18 :         auto& member2 = members[p.first];
     848          18 :         auto& skContribution = p.second;
     849             : 
     850          18 :         bool result = (resultIt++)->get();
     851          18 :         if (!result) {
     852          18 :             logger.Batch("  %s did send an invalid justification for %s", member->dmn->proTxHash.ToString(), member2->dmn->proTxHash.ToString());
     853           6 :             MarkBadMember(member);
     854             :         } else {
     855          36 :             logger.Batch("  %s justified for %s", member->dmn->proTxHash.ToString(), member2->dmn->proTxHash.ToString());
     856          24 :             if (AreWeMember() && member2->id == myId) {
     857           4 :                 receivedSkContributions[member->idx] = skContribution;
     858           4 :                 member->weComplain = false;
     859             : 
     860           4 :                 dkgManager.WriteVerifiedSkContribution(params.type, pindexQuorum, member->dmn->proTxHash, skContribution);
     861             :             }
     862          18 :             member->complaintsFromOthers.erase(member2->dmn->proTxHash);
     863             :         }
     864             :     }
     865             : 
     866          12 :     int receivedCount = 0;
     867          12 :     int expectedCount = 0;
     868             : 
     869          48 :     for (const auto& m : members) {
     870          36 :         if (!m->justifications.empty()) {
     871          15 :             receivedCount++;
     872             :         }
     873             : 
     874          36 :         if (m->someoneComplain) {
     875          18 :             expectedCount++;
     876             :         }
     877             :     }
     878             : 
     879          24 :     logger.Batch("verified justification: received=%d/%d time=%d", receivedCount, expectedCount, t1.count());
     880             : }
     881             : 
     882         106 : void CDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages)
     883             : {
     884         212 :     if (!AreWeMember()) {
     885          51 :         return;
     886             :     }
     887             : 
     888         110 :     CDKGLogger logger(*this, __func__);
     889             : 
     890         110 :     std::vector<size_t> badMembers;
     891          55 :     std::vector<size_t> openComplaintMembers;
     892             : 
     893         220 :     for (const auto& m : members) {
     894         165 :         if (m->bad) {
     895          17 :             badMembers.emplace_back(m->idx);
     896          17 :             continue;
     897             :         }
     898         148 :         if (!m->complaintsFromOthers.empty()) {
     899           3 :             MarkBadMember(m.get());
     900           3 :             openComplaintMembers.emplace_back(m->idx);
     901             :         }
     902             :     }
     903             : 
     904          55 :     if (!badMembers.empty() || !openComplaintMembers.empty()) {
     905          32 :         logger.Batch("verification result:");
     906             :     }
     907          55 :     if (!badMembers.empty()) {
     908          13 :         logger.Batch("  members previously determined as bad:");
     909          30 :         for (const auto& idx : badMembers) {
     910          51 :             logger.Batch("    %s", members[idx]->dmn->proTxHash.ToString());
     911             :         }
     912             :     }
     913          55 :     if (!openComplaintMembers.empty()) {
     914           3 :         logger.Batch("  members with open complaints and now marked as bad:");
     915           6 :         for (const auto& idx : openComplaintMembers) {
     916           9 :             logger.Batch("    %s", members[idx]->dmn->proTxHash.ToString());
     917             :         }
     918             :     }
     919             : 
     920          55 :     logger.Flush();
     921             : 
     922          55 :     SendCommitment(pendingMessages);
     923             : }
     924             : 
     925          55 : void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages)
     926             : {
     927         106 :     CDKGLogger logger(*this, __func__);
     928             : 
     929         110 :     assert(AreWeMember());
     930             : 
     931          55 :     logger.Batch("sending commitment");
     932             : 
     933         106 :     CDKGPrematureCommitment qc(params);
     934          55 :     qc.llmqType = (uint8_t)params.type;
     935          55 :     qc.quorumHash = pindexQuorum->GetBlockHash();
     936          55 :     qc.proTxHash = myProTxHash;
     937             : 
     938         220 :     for (size_t i = 0; i < members.size(); i++) {
     939         165 :         auto& m = members[i];
     940         165 :         if (!m->bad) {
     941         145 :             qc.validMembers[i] = true;
     942             :         }
     943             :     }
     944             : 
     945          55 :     if (qc.CountValidMembers() < params.minSize) {
     946           3 :         logger.Batch("not enough valid members. not sending commitment");
     947           7 :         return;
     948             :     }
     949             : 
     950          52 :     if (ShouldSimulateError("commit-omit")) {
     951           1 :         logger.Batch("omitting");
     952           1 :         return;
     953             :     }
     954             : 
     955          51 :     cxxtimer::Timer timerTotal(true);
     956             : 
     957         102 :     cxxtimer::Timer t1(true);
     958         102 :     std::vector<uint16_t> memberIndexes;
     959          51 :     std::vector<BLSVerificationVectorPtr> vvecs;
     960         102 :     BLSSecretKeyVector skContributions;
     961          51 :     if (!dkgManager.GetVerifiedContributions(params.type, pindexQuorum, qc.validMembers, memberIndexes, vvecs, skContributions)) {
     962           0 :         logger.Batch("failed to get valid contributions");
     963           0 :         return;
     964             :     }
     965             : 
     966         102 :     BLSVerificationVectorPtr vvec = cache.BuildQuorumVerificationVector(::SerializeHash(memberIndexes), vvecs);
     967          51 :     if (vvec == nullptr) {
     968           0 :         logger.Batch("failed to build quorum verification vector");
     969           0 :         return;
     970             :     }
     971          51 :     t1.stop();
     972             : 
     973         102 :     cxxtimer::Timer t2(true);
     974         102 :     CBLSSecretKey skShare = cache.AggregateSecretKeys(::SerializeHash(memberIndexes), skContributions);
     975          51 :     if (!skShare.IsValid()) {
     976           0 :         logger.Batch("failed to build own secret share");
     977           0 :         return;
     978             :     }
     979          51 :     t2.stop();
     980             : 
     981         102 :     logger.Batch("pubKeyShare=%s", bls::EncodePublic(Params(), skShare.GetPublicKey()));
     982             : 
     983         102 :     cxxtimer::Timer t3(true);
     984          51 :     qc.quorumPublicKey = (*vvec)[0];
     985          51 :     qc.quorumVvecHash = ::SerializeHash(*vvec);
     986             : 
     987          51 :     int lieType = -1;
     988          51 :     if (ShouldSimulateError("commit-lie")) {
     989           1 :         lieType = GetRandInt(5);
     990           2 :         logger.Batch("lying on commitment. lieType=%d", lieType);
     991             :     }
     992             : 
     993          51 :     if (lieType == 0) {
     994           0 :         CBLSSecretKey k;
     995           0 :         k.MakeNewKey();
     996           0 :         qc.quorumPublicKey = k.GetPublicKey();
     997          51 :     } else if (lieType == 1) {
     998           0 :         (*qc.quorumVvecHash.begin())++;
     999             :     }
    1000             : 
    1001          51 :     uint256 commitmentHash = utils::BuildCommitmentHash((Consensus::LLMQType)qc.llmqType, qc.quorumHash, qc.validMembers, qc.quorumPublicKey, qc.quorumVvecHash);
    1002             : 
    1003          51 :     if (lieType == 2) {
    1004           0 :         (*commitmentHash.begin())++;
    1005             :     }
    1006             : 
    1007          51 :     qc.sig = activeMasternodeManager->OperatorKey()->Sign(commitmentHash);
    1008          51 :     qc.quorumSig = skShare.Sign(commitmentHash);
    1009             : 
    1010          51 :     if (lieType == 3) {
    1011           0 :         std::vector<uint8_t> buf = qc.sig.ToByteVector();
    1012           0 :         buf[5]++;
    1013           0 :         qc.sig.SetByteVector(buf);
    1014          51 :     } else if (lieType == 4) {
    1015           2 :         std::vector<uint8_t> buf = qc.quorumSig.ToByteVector();
    1016           1 :         buf[5]++;
    1017           1 :         qc.quorumSig.SetByteVector(buf);
    1018             :     }
    1019             : 
    1020          51 :     t3.stop();
    1021          51 :     timerTotal.stop();
    1022             : 
    1023          51 :     logger.Batch("built premature commitment. time1=%d, time2=%d, time3=%d, totalTime=%d",
    1024          51 :                     t1.count(), t2.count(), t3.count(), timerTotal.count());
    1025             : 
    1026          51 :     logger.Flush();
    1027             : 
    1028          51 :     quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) {
    1029          51 :         status.sentPrematureCommitment = true;
    1030          51 :         return true;
    1031             :     });
    1032             : 
    1033          51 :     pendingMessages.PushPendingMessage(-1, qc, MSG_QUORUM_PREMATURE_COMMITMENT);
    1034             : }
    1035             : 
    1036             : // only performs cheap verifications, but not the signature of the message. this is checked with batched verification
    1037         141 : bool CDKGSession::PreVerifyMessage(const CDKGPrematureCommitment& qc, bool& retBan) const
    1038             : {
    1039         282 :     CDKGLogger logger(*this, __func__);
    1040             : 
    1041         141 :     retBan = false;
    1042             : 
    1043         141 :     if (qc.quorumHash != pindexQuorum->GetBlockHash()) {
    1044           0 :         logger.Batch("commitment for wrong quorum, rejecting");
    1045           0 :         return false;
    1046             :     }
    1047             : 
    1048         141 :     auto member = GetMember(qc.proTxHash);
    1049         141 :     if (!member) {
    1050           0 :         logger.Batch("committer not a member of this quorum, rejecting premature commitment");
    1051           0 :         retBan = true;
    1052           0 :         return false;
    1053             :     }
    1054             : 
    1055         141 :     if (qc.validMembers.size() != (size_t)params.size) {
    1056           0 :         logger.Batch("invalid validMembers bitset size");
    1057           0 :         retBan = true;
    1058           0 :         return false;
    1059             :     }
    1060             : 
    1061         141 :     if (qc.CountValidMembers() < params.minSize) {
    1062           0 :         logger.Batch("invalid validMembers count. validMembersCount=%d", qc.CountValidMembers());
    1063           0 :         retBan = true;
    1064           0 :         return false;
    1065             :     }
    1066         141 :     if (!qc.sig.IsValid()) {
    1067           0 :         logger.Batch("invalid membersSig");
    1068           0 :         retBan = true;
    1069           0 :         return false;
    1070             :     }
    1071         141 :     if (!qc.quorumSig.IsValid()) {
    1072           1 :         logger.Batch("invalid quorumSig");
    1073           1 :         retBan = true;
    1074           1 :         return false;
    1075             :     }
    1076             : 
    1077         140 :     for (size_t i = members.size(); i < (size_t)params.size; i++) {
    1078           0 :         if (qc.validMembers[i]) {
    1079           0 :             retBan = true;
    1080           0 :             logger.Batch("invalid validMembers bitset. bit %d should not be set", i);
    1081           0 :             return false;
    1082             :         }
    1083             :     }
    1084             : 
    1085         140 :     if (member->prematureCommitments.size() >= 2) {
    1086             :         // don't do any further processing if we got more than 1 valid commitment already
    1087             :         // this is a DoS protection against members sending multiple commitments with valid signatures to us
    1088             :         // we must bail out before any expensive BLS verification happens
    1089           0 :         logger.Batch("dropping commitment from %s as we already got %d commitments",
    1090           0 :                       member->dmn->proTxHash.ToString(), member->prematureCommitments.size());
    1091           0 :         return false;
    1092             :     }
    1093             : 
    1094             :     return true;
    1095             : }
    1096             : 
    1097         140 : void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGPrematureCommitment& qc, bool& retBan)
    1098             : {
    1099         280 :     CDKGLogger logger(*this, __func__);
    1100             : 
    1101         140 :     retBan = false;
    1102             : 
    1103         280 :     cxxtimer::Timer t1(true);
    1104             : 
    1105         280 :     logger.Batch("received premature commitment from %s. validMembers=%d", qc.proTxHash.ToString(), qc.CountValidMembers());
    1106             : 
    1107         140 :     auto member = GetMember(qc.proTxHash);
    1108             : 
    1109         140 :     {
    1110         140 :         LOCK(invCs);
    1111             : 
    1112             :         // keep track of ALL commitments but only relay valid ones (or if we couldn't build the vvec)
    1113             :         // relaying is done further down
    1114         140 :         prematureCommitments.emplace(hash, qc);
    1115         140 :         member->prematureCommitments.emplace(hash);
    1116             :     }
    1117             : 
    1118         280 :     std::vector<uint16_t> memberIndexes;
    1119         140 :     std::vector<BLSVerificationVectorPtr> vvecs;
    1120           0 :     BLSSecretKeyVector skContributions;
    1121         140 :     BLSVerificationVectorPtr quorumVvec;
    1122         140 :     if (dkgManager.GetVerifiedContributions(params.type, pindexQuorum, qc.validMembers, memberIndexes, vvecs, skContributions)) {
    1123         280 :         quorumVvec = cache.BuildQuorumVerificationVector(::SerializeHash(memberIndexes), vvecs);
    1124             :     }
    1125             : 
    1126         140 :     if (quorumVvec == nullptr) {
    1127           0 :         logger.Batch("failed to build quorum verification vector. skipping full verification");
    1128             :         // we might be the unlucky one who didn't receive all contributions, but we still have to relay
    1129             :         // the premature commitment as others might be luckier
    1130             :     } else {
    1131             :         // we got all information that is needed to verify everything (even though we might not be a member of the quorum)
    1132             :         // if any of this verification fails, we won't relay this message. This ensures that invalid messages are lost
    1133             :         // in the network. Nodes relaying such invalid messages to us are not punished as they might have not known
    1134             :         // all contributions. We only handle up to 2 commitments per member, so a DoS shouldn't be possible
    1135             : 
    1136         140 :         if ((*quorumVvec)[0] != qc.quorumPublicKey) {
    1137           0 :             logger.Batch("calculated quorum public key does not match");
    1138           0 :             return;
    1139             :         }
    1140         140 :         uint256 vvecHash = ::SerializeHash(*quorumVvec);
    1141         140 :         if (qc.quorumVvecHash != vvecHash) {
    1142           0 :             logger.Batch("calculated quorum vvec hash does not match");
    1143           0 :             return;
    1144             :         }
    1145             : 
    1146         280 :         CBLSPublicKey pubKeyShare = cache.BuildPubKeyShare(::SerializeHash(std::make_pair(memberIndexes, member->id)), quorumVvec, member->id);
    1147         140 :         if (!pubKeyShare.IsValid()) {
    1148           0 :             logger.Batch("failed to calculate public key share");
    1149           0 :             return;
    1150             :         }
    1151             : 
    1152         140 :         if (!qc.quorumSig.VerifyInsecure(pubKeyShare, qc.GetSignHash())) {
    1153           0 :             logger.Batch("failed to verify quorumSig");
    1154           0 :             return;
    1155             :         }
    1156             :     }
    1157             : 
    1158         280 :     LOCK(invCs);
    1159         140 :     validCommitments.emplace(hash);
    1160             : 
    1161         140 :     CInv inv(MSG_QUORUM_PREMATURE_COMMITMENT, hash);
    1162         140 :     RelayInvToParticipants(inv);
    1163             : 
    1164         140 :     quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) {
    1165         140 :         status.receivedPrematureCommitment = true;
    1166         140 :         return true;
    1167             :     });
    1168             : 
    1169         140 :     int receivedCount = 0;
    1170         560 :     for (const auto& m : members) {
    1171         420 :         if (!m->prematureCommitments.empty()) {
    1172         267 :             receivedCount++;
    1173             :         }
    1174             :     }
    1175             : 
    1176         140 :     t1.stop();
    1177             : 
    1178         280 :     logger.Batch("verified premature commitment. received=%d/%d, time=%d", receivedCount, members.size(), t1.count());
    1179             : }
    1180             : 
    1181         104 : std::vector<CFinalCommitment> CDKGSession::FinalizeCommitments()
    1182             : {
    1183         208 :     if (!AreWeMember()) {
    1184          51 :         return {};
    1185             :     }
    1186             : 
    1187         157 :     CDKGLogger logger(*this, __func__);
    1188             : 
    1189         106 :     cxxtimer::Timer totalTimer(true);
    1190             : 
    1191         106 :     std::vector<CBLSPublicKey> allkeys;
    1192         212 :     for (const auto& m : members) {
    1193         159 :         allkeys.emplace_back(m->dmn->pdmnState->pubKeyOperator.Get());
    1194             :     }
    1195             : 
    1196          53 :     typedef std::vector<bool> Key;
    1197         106 :     std::map<Key, std::vector<CDKGPrematureCommitment>> commitmentsMap;
    1198             : 
    1199         193 :     for (const auto& p : prematureCommitments) {
    1200         140 :         auto& qc = p.second;
    1201         140 :         if (!validCommitments.count(p.first)) {
    1202           0 :             continue;
    1203             :         }
    1204             : 
    1205             :         // should have been verified before
    1206         140 :         assert(qc.CountValidMembers() >= params.minSize);
    1207             : 
    1208         140 :         auto it = commitmentsMap.find(qc.validMembers);
    1209         140 :         if (it == commitmentsMap.end()) {
    1210          52 :             it = commitmentsMap.emplace(qc.validMembers, std::vector<CDKGPrematureCommitment>()).first;
    1211             :         }
    1212             : 
    1213         140 :         it->second.emplace_back(qc);
    1214             :     }
    1215             : 
    1216          53 :     const CChainParams& chainparams = Params();
    1217             : 
    1218         106 :     std::vector<CFinalCommitment> finalCommitments;
    1219         105 :     for (const auto& p : commitmentsMap) {
    1220          52 :         auto& cvec = p.second;
    1221          52 :         if (cvec.size() < (size_t)params.minSize) {
    1222             :             // commitment was signed by a minority
    1223           3 :             continue;
    1224             :         }
    1225             : 
    1226          98 :         std::vector<CBLSId> signerIds;
    1227          49 :         std::vector<CBLSSignature> thresholdSigs;
    1228             : 
    1229          49 :         auto& first = cvec[0];
    1230             : 
    1231          98 :         CFinalCommitment fqc(params, first.quorumHash);
    1232          49 :         fqc.validMembers = first.validMembers;
    1233          49 :         fqc.quorumPublicKey = first.quorumPublicKey;
    1234          49 :         fqc.quorumVvecHash = first.quorumVvecHash;
    1235             : 
    1236          49 :         uint256 commitmentHash = utils::BuildCommitmentHash((Consensus::LLMQType)fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, fqc.quorumVvecHash);
    1237             : 
    1238          98 :         std::vector<CBLSSignature> aggSigs;
    1239          49 :         std::vector<CBLSPublicKey> aggPks;
    1240          49 :         aggSigs.reserve(cvec.size());
    1241          49 :         aggPks.reserve(cvec.size());
    1242             : 
    1243         186 :         for (size_t i = 0; i < cvec.size(); i++) {
    1244         137 :             auto& qc = cvec[i];
    1245             : 
    1246         274 :             if (qc.quorumPublicKey != first.quorumPublicKey || qc.quorumVvecHash != first.quorumVvecHash) {
    1247           0 :                 logger.Batch("quorumPublicKey or quorumVvecHash does not match, skipping");
    1248           0 :                 continue;
    1249             :             }
    1250             : 
    1251         137 :             size_t signerIndex = membersMap[qc.proTxHash];
    1252         137 :             const auto& m = members[signerIndex];
    1253             : 
    1254         137 :             fqc.signers[signerIndex] = true;
    1255         137 :             aggSigs.emplace_back(qc.sig);
    1256         137 :             aggPks.emplace_back(m->dmn->pdmnState->pubKeyOperator.Get());
    1257             : 
    1258         137 :             signerIds.emplace_back(m->id);
    1259         137 :             thresholdSigs.emplace_back(qc.quorumSig);
    1260             :         }
    1261             : 
    1262          49 :         cxxtimer::Timer t1(true);
    1263          49 :         fqc.membersSig = CBLSSignature::AggregateSecure(aggSigs, aggPks, commitmentHash);
    1264          49 :         t1.stop();
    1265             : 
    1266          49 :         cxxtimer::Timer t2(true);
    1267          49 :         if (!fqc.quorumSig.Recover(thresholdSigs, signerIds)) {
    1268           0 :             logger.Batch("failed to recover quorum sig");
    1269           0 :             continue;
    1270             :         }
    1271          49 :         t2.stop();
    1272             : 
    1273          98 :         cxxtimer::Timer t3(true);
    1274          49 :         if (!fqc.Verify(allkeys, params)) {
    1275           0 :             logger.Batch("failed to verify final commitment");
    1276           0 :             continue;
    1277             :         }
    1278          49 :         t3.stop();
    1279             : 
    1280          49 :         finalCommitments.emplace_back(fqc);
    1281             : 
    1282          49 :         logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s, time1=%d, time2=%d, time3=%d, total=%d",
    1283          98 :                         fqc.CountValidMembers(), fqc.CountSigners(), bls::EncodePublic(chainparams, fqc.quorumPublicKey),
    1284          98 :                         t1.count(), t2.count(), t3.count(), totalTimer.count());
    1285             :     }
    1286             : 
    1287          53 :     totalTimer.stop();
    1288          53 :     logger.Flush();
    1289             : 
    1290          53 :     return finalCommitments;
    1291             : }
    1292             : 
    1293        1159 : CDKGMember* CDKGSession::GetMember(const uint256& proTxHash) const
    1294             : {
    1295        1159 :     auto it = membersMap.find(proTxHash);
    1296        1159 :     if (it == membersMap.end()) {
    1297             :         return nullptr;
    1298             :     }
    1299        1159 :     return members[it->second].get();
    1300             : }
    1301             : 
    1302          36 : void CDKGSession::MarkBadMember(CDKGMember* member)
    1303             : {
    1304          36 :     if (member->bad) {
    1305             :         return;
    1306             :     }
    1307          33 :     quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) {
    1308          33 :         status.bad = true;
    1309          33 :         return true;
    1310             :     });
    1311          33 :     member->bad = true;
    1312             : }
    1313             : 
    1314         372 : void CDKGSession::RelayInvToParticipants(const CInv& inv) const
    1315             : {
    1316         372 :     LOCK(invCs);
    1317         372 :     g_connman->ForEachNode([&](CNode* pnode) {
    1318        7787 :         if (!pnode->verifiedProRegTxHash.IsNull() && relayMembers.count(pnode->verifiedProRegTxHash)) {
    1319         705 :             pnode->PushInventory(inv);
    1320             :         }
    1321        3680 :     });
    1322         372 : }
    1323             : 
    1324             : }

Generated by: LCOV version 1.14