LCOV - code coverage report
Current view: top level - src/bls - bls_batchverifier.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 108 118 91.5 %
Date: 2026-06-25 07:23:43 Functions: 39 46 84.8 %

          Line data    Source code
       1             : // Copyright (c) 2018-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #ifndef DASH_CRYPTO_BLS_BATCHVERIFIER_H
       6             : #define DASH_CRYPTO_BLS_BATCHVERIFIER_H
       7             : 
       8             : #include <bls/bls.h>
       9             : 
      10             : #include <map>
      11             : #include <vector>
      12             : 
      13             : template<typename SourceId, typename MessageId>
      14             : class CBLSBatchVerifier
      15             : {
      16             : private:
      17             :     struct Message {
      18             :         MessageId msgId;
      19             :         uint256 msgHash;
      20             :         CBLSSignature sig;
      21             :         CBLSPublicKey pubKey;
      22             :     };
      23             : 
      24             :     using MessageMap = std::map<MessageId, Message>;
      25             :     using MessageMapIterator = typename MessageMap::iterator;
      26             :     using MessagesBySourceMap = std::map<SourceId, std::vector<MessageMapIterator>>;
      27             : 
      28             :     bool secureVerification;
      29             :     bool perMessageFallback;
      30             :     size_t subBatchSize;
      31             : 
      32             :     MessageMap messages;
      33             :     MessagesBySourceMap messagesBySource;
      34             : 
      35             : public:
      36             :     std::set<SourceId> badSources;
      37             :     std::set<MessageId> badMessages;
      38             : 
      39             : public:
      40       75873 :     CBLSBatchVerifier(bool _secureVerification, bool _perMessageFallback, size_t _subBatchSize = 0) :
      41       25291 :             secureVerification(_secureVerification),
      42       25291 :             perMessageFallback(_perMessageFallback),
      43       25291 :             subBatchSize(_subBatchSize)
      44       25291 :     {
      45       50582 :     }
      46             : 
      47       45877 :     void PushMessage(const SourceId& sourceId, const MessageId& msgId, const uint256& msgHash, const CBLSSignature& sig, const CBLSPublicKey& pubKey)
      48             :     {
      49       45877 :         assert(sig.IsValid() && pubKey.IsValid());
      50             : 
      51       45877 :         auto it = messages.emplace(msgId, Message{msgId, msgHash, sig, pubKey}).first;
      52       45877 :         messagesBySource[sourceId].emplace_back(it);
      53             : 
      54       45877 :         if (subBatchSize != 0 && messages.size() >= subBatchSize) {
      55           0 :             Verify();
      56           0 :             ClearMessages();
      57           0 :         }
      58       45877 :     }
      59             : 
      60           0 :     void ClearMessages()
      61             :     {
      62           0 :         messages.clear();
      63           0 :         messagesBySource.clear();
      64           0 :     }
      65             : 
      66         805 :     size_t GetUniqueSourceCount() const
      67             :     {
      68         805 :         return messagesBySource.size();
      69             :     }
      70             : 
      71       25291 :     void Verify()
      72             :     {
      73       25291 :         std::map<uint256, std::vector<MessageMapIterator>> byMessageHash;
      74             : 
      75       67253 :         for (auto it = messages.begin(); it != messages.end(); ++it) {
      76       41962 :             byMessageHash[it->second.msgHash].emplace_back(it);
      77       41962 :         }
      78             : 
      79       25291 :         if (VerifyBatch(byMessageHash)) {
      80             :             // full batch is valid
      81       25255 :             return;
      82             :         }
      83             : 
      84             :         // revert to per-source verification
      85         512 :         for (const auto& [from, message_map] : messagesBySource) {
      86         166 :             bool batchValid = false;
      87             : 
      88             :             // no need to verify it again if there was just one source
      89         166 :             if (messagesBySource.size() != 1) {
      90         164 :                 byMessageHash.clear();
      91         416 :                 for (auto it = message_map.begin(); it != message_map.end(); ++it) {
      92         252 :                     byMessageHash[(*it)->second.msgHash].emplace_back(*it);
      93         252 :                 }
      94         164 :                 batchValid = VerifyBatch(byMessageHash);
      95         164 :             }
      96         166 :             if (!batchValid) {
      97          76 :                 badSources.emplace(from);
      98             : 
      99          38 :                 if (perMessageFallback) {
     100             :                     // revert to per-message verification
     101          20 :                     if (message_map.size() == 1) {
     102             :                         // no need to re-verify a single message
     103          32 :                         badMessages.emplace(message_map[0]->second.msgId);
     104          16 :                     } else {
     105          20 :                         for (const auto& msgIt : message_map) {
     106          16 :                             if (badMessages.count(msgIt->first)) {
     107             :                                 // same message might be invalid from different source, so no need to re-verify it
     108           0 :                                 continue;
     109             :                             }
     110             : 
     111          16 :                             const auto& msg = msgIt->second;
     112          16 :                             if (!msg.sig.VerifyInsecure(msg.pubKey, msg.msgHash)) {
     113           4 :                                 badMessages.emplace(msg.msgId);
     114           4 :                             }
     115             :                         }
     116             :                     }
     117          20 :                 }
     118          38 :             }
     119             :         }
     120       25291 :     }
     121             : 
     122             : private:
     123             :     // All Verify methods take ownership of the passed byMessageHash map and thus might modify the map. This is to avoid
     124             :     // unnecessary copies
     125             : 
     126       25455 :     bool VerifyBatch(std::map<uint256, std::vector<MessageMapIterator>>& byMessageHash)
     127             :     {
     128       25455 :         if (secureVerification) {
     129         108 :             return VerifyBatchSecure(byMessageHash);
     130             :         } else {
     131       25347 :             return VerifyBatchInsecure(byMessageHash);
     132             :         }
     133       25455 :     }
     134             : 
     135       25347 :     bool VerifyBatchInsecure(const std::map<uint256, std::vector<MessageMapIterator>>& byMessageHash)
     136             :     {
     137       25347 :         std::vector<CBLSSignature> sigsToAggregate;
     138       25347 :         std::vector<uint256> msgHashes;
     139       25347 :         std::vector<CBLSPublicKey> pubKeys;
     140       25347 :         std::set<MessageId> dups;
     141             : 
     142       25347 :         msgHashes.reserve(messages.size());
     143       25347 :         pubKeys.reserve(messages.size());
     144       25347 :         sigsToAggregate.reserve(messages.size());
     145             : 
     146       25347 :         std::vector<CBLSPublicKey> pubKeysToAggregate;
     147      102049 :         for (const auto& [msgHash, vec_message_it] : byMessageHash) {
     148       38351 :             pubKeysToAggregate.clear();
     149       38351 :             pubKeysToAggregate.reserve(vec_message_it.size());
     150             : 
     151       80257 :             for (const auto& msgIt : vec_message_it) {
     152       41906 :                 const auto& msg = msgIt->second;
     153             : 
     154       41906 :                 if (!dups.emplace(msg.msgId).second) {
     155           0 :                     continue;
     156             :                 }
     157             : 
     158       41906 :                 sigsToAggregate.push_back(msg.sig);
     159       41906 :                 pubKeysToAggregate.push_back(msg.pubKey);
     160             :             }
     161             : 
     162       38351 :             CBLSPublicKey aggPubKey = CBLSPublicKey::AggregateInsecure(pubKeysToAggregate);
     163             : 
     164       38351 :             if (!aggPubKey.IsValid()) {
     165             :                 // only duplicates for this msgHash
     166           0 :                 continue;
     167             :             }
     168             : 
     169       38351 :             msgHashes.emplace_back(msgHash);
     170       38351 :             pubKeys.emplace_back(aggPubKey);
     171             :         }
     172             : 
     173       25347 :         if (msgHashes.empty()) {
     174         556 :             return true;
     175             :         }
     176             : 
     177       24791 :         CBLSSignature aggSig = CBLSSignature::AggregateInsecure(sigsToAggregate);
     178       24791 :         return aggSig.VerifyInsecureAggregated(pubKeys, msgHashes);
     179       25347 :     }
     180             : 
     181         108 :     bool VerifyBatchSecure(std::map<uint256, std::vector<MessageMapIterator>>& byMessageHash)
     182             :     {
     183             :         // Loop until the byMessageHash map is empty, which means that all messages were verified
     184             :         // The secure form of verification will only aggregate one message for the same message hash, even if multiple
     185             :         // exist (signed with different keys). This avoids the rogue public key attack.
     186             :         // This is slower than the insecure form as it requires more pairings
     187         224 :         while (!byMessageHash.empty()) {
     188         148 :             if (!VerifyBatchSecureStep(byMessageHash)) {
     189          32 :                 return false;
     190             :             }
     191             :         }
     192          76 :         return true;
     193         108 :     }
     194             : 
     195         148 :     bool VerifyBatchSecureStep(std::map<uint256, std::vector<MessageMapIterator>>& byMessageHash)
     196             :     {
     197         148 :         std::vector<CBLSSignature> sigsToAggregate;
     198         148 :         std::vector<uint256> msgHashes;
     199         148 :         std::vector<CBLSPublicKey> pubKeys;
     200         148 :         std::set<MessageId> dups;
     201             : 
     202         148 :         msgHashes.reserve(messages.size());
     203         148 :         pubKeys.reserve(messages.size());
     204         148 :         sigsToAggregate.reserve(messages.size());
     205             : 
     206         420 :         for (auto it = byMessageHash.begin(); it != byMessageHash.end(); ) {
     207         272 :             const auto& msgHash = it->first;
     208         272 :             auto& messageIts = it->second;
     209         272 :             const auto& msg = messageIts.back()->second;
     210             : 
     211         272 :             if (dups.emplace(msg.msgId).second) {
     212         272 :                 msgHashes.emplace_back(msgHash);
     213         272 :                 pubKeys.emplace_back(msg.pubKey);
     214         272 :                 sigsToAggregate.push_back(msg.sig);
     215         272 :             }
     216             : 
     217         272 :             messageIts.pop_back();
     218         272 :             if (messageIts.empty()) {
     219         224 :                 it = byMessageHash.erase(it);
     220         224 :             } else {
     221          48 :                 ++it;
     222             :             }
     223             :         }
     224             : 
     225         148 :         assert(!msgHashes.empty());
     226             : 
     227         148 :         CBLSSignature aggSig = CBLSSignature::AggregateInsecure(sigsToAggregate);
     228         148 :         return aggSig.VerifyInsecureAggregated(pubKeys, msgHashes);
     229         148 :     }
     230             : };
     231             : 
     232             : #endif //DASH_CRYPTO_BLS_BATCHVERIFIER_H

Generated by: LCOV version 1.16