LCOV - code coverage report
Current view: top level - src/llmq - dkgsession.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 77 95 81.1 %
Date: 2026-06-25 07:23:43 Functions: 117 131 89.3 %

          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 BITCOIN_LLMQ_DKGSESSION_H
       6             : #define BITCOIN_LLMQ_DKGSESSION_H
       7             : 
       8             : #include <protocol.h>
       9             : 
      10             : #include <batchedlogger.h>
      11             : #include <bls/bls.h>
      12             : #include <bls/bls_ies.h>
      13             : #include <bls/bls_worker.h>
      14             : #include <evo/types.h>
      15             : #include <llmq/params.h>
      16             : #include <util/std23.h>
      17             : 
      18             : #include <saltedhasher.h>
      19             : 
      20             : #include <sync.h>
      21             : 
      22             : #include <optional>
      23             : 
      24             : class CConnman;
      25             : class CBLSWorker;
      26             : class CDeterministicMNManager;
      27             : class ChainstateManager;
      28             : class CBlockIndex;
      29             : namespace llmq {
      30             : class CDKGDebugManager;
      31             : class CDKGSession;
      32             : class CDKGSessionManager;
      33             : class CFinalCommitment;
      34             : class CQuorumSnapshotManager;
      35             : } // namespace llmq
      36             : 
      37             : namespace llmq {
      38       18345 : class CDKGContribution
      39             : {
      40             : public:
      41             :     Consensus::LLMQType llmqType;
      42             :     uint256 quorumHash;
      43             :     uint256 proTxHash;
      44             :     BLSVerificationVectorPtr vvec;
      45             :     std::shared_ptr<CBLSIESMultiRecipientObjects<CBLSSecretKey>> contributions;
      46             :     CBLSSignature sig;
      47             : 
      48             : public:
      49             :     template<typename Stream>
      50       29625 :     inline void SerializeWithoutSig(Stream& s) const
      51             :     {
      52       29625 :         s << std23::to_underlying(llmqType);
      53       29625 :         s << quorumHash;
      54       29625 :         s << proTxHash;
      55       29625 :         s << *vvec;
      56       29625 :         s << *contributions;
      57       29625 :     }
      58             :     template<typename Stream>
      59       18071 :     inline void Serialize(Stream& s) const
      60             :     {
      61       18071 :         SerializeWithoutSig(s);
      62       18071 :         s << sig;
      63       18071 :     }
      64             :     template<typename Stream>
      65        8951 :     inline void Unserialize(Stream& s)
      66             :     {
      67        8951 :         std::vector<CBLSPublicKey> tmp1;
      68        8951 :         CBLSIESMultiRecipientObjects<CBLSSecretKey> tmp2;
      69             : 
      70        8951 :         s >> llmqType;
      71        8951 :         s >> quorumHash;
      72        8952 :         s >> proTxHash;
      73        8950 :         s >> tmp1;
      74        8952 :         s >> tmp2;
      75        8952 :         s >> sig;
      76             : 
      77        8952 :         vvec = std::make_shared<std::vector<CBLSPublicKey>>(std::move(tmp1));
      78        8951 :         contributions = std::make_shared<CBLSIESMultiRecipientObjects<CBLSSecretKey>>(std::move(tmp2));
      79        8957 :     }
      80             : 
      81       11554 :     [[nodiscard]] uint256 GetSignHash() const
      82             :     {
      83       11554 :         CHashWriter hw(SER_GETHASH, 0);
      84       11554 :         SerializeWithoutSig(hw);
      85       11554 :         hw << CBLSSignature();
      86       11554 :         return hw.GetHash();
      87           0 :     }
      88             : };
      89             : 
      90        3496 : class CDKGComplaint
      91             : {
      92             : public:
      93        5156 :     Consensus::LLMQType llmqType{Consensus::LLMQType::LLMQ_NONE};
      94             :     uint256 quorumHash;
      95             :     uint256 proTxHash;
      96             :     std::vector<bool> badMembers;
      97             :     std::vector<bool> complainForMembers;
      98             :     CBLSSignature sig;
      99             : 
     100             : public:
     101        8083 :     CDKGComplaint() = default;
     102        7386 :     explicit CDKGComplaint(const Consensus::LLMQParams& params) :
     103        7386 :             badMembers((size_t)params.size), complainForMembers((size_t)params.size) {};
     104             : 
     105       19704 :     SERIALIZE_METHODS(CDKGComplaint, obj)
     106             :     {
     107        6568 :         READWRITE(
     108             :                 obj.llmqType,
     109             :                 obj.quorumHash,
     110             :                 obj.proTxHash,
     111             :                 DYNBITSET(obj.badMembers),
     112             :                 DYNBITSET(obj.complainForMembers),
     113             :                 obj.sig
     114             :                 );
     115        6568 :     }
     116             : 
     117        2015 :     [[nodiscard]] uint256 GetSignHash() const
     118             :     {
     119        2015 :         CDKGComplaint tmp(*this);
     120        2015 :         tmp.sig = CBLSSignature();
     121        2015 :         return ::SerializeHash(tmp);
     122        2015 :     }
     123             : };
     124             : 
     125          50 : class CDKGJustification
     126             : {
     127             : public:
     128             :     Consensus::LLMQType llmqType;
     129             :     uint256 quorumHash;
     130             :     uint256 proTxHash;
     131             :     struct Contribution {
     132             :         uint32_t index;
     133             :         CBLSSecretKey key;
     134         468 :         SERIALIZE_METHODS(Contribution, obj)
     135             :         {
     136         156 :             READWRITE(obj.index, obj.key);
     137         156 :         }
     138             :     };
     139             :     std::vector<Contribution> contributions;
     140             :     CBLSSignature sig;
     141             : 
     142             : public:
     143         312 :     SERIALIZE_METHODS(CDKGJustification, obj)
     144             :     {
     145         104 :         READWRITE(obj.llmqType, obj.quorumHash, obj.proTxHash, obj.contributions, obj.sig);
     146         104 :     }
     147             : 
     148          32 :     [[nodiscard]] uint256 GetSignHash() const
     149             :     {
     150          32 :         CDKGJustification tmp(*this);
     151          32 :         tmp.sig = CBLSSignature();
     152          32 :         return ::SerializeHash(tmp);
     153          32 :     }
     154             : };
     155             : 
     156             : // each member commits to a single set of valid members with this message
     157             : // then each node aggregate all received premature commitments
     158             : // into a single CFinalCommitment, which is only valid if
     159             : // enough (>=minSize) premature commitments were aggregated
     160             : class CDKGPrematureCommitment
     161             : {
     162             : public:
     163       15416 :     Consensus::LLMQType llmqType{Consensus::LLMQType::LLMQ_NONE};
     164             :     uint256 quorumHash;
     165             :     uint256 proTxHash;
     166             :     std::vector<bool> validMembers;
     167             : 
     168             :     CBLSPublicKey quorumPublicKey;
     169             :     uint256 quorumVvecHash;
     170             : 
     171             :     CBLSSignature quorumSig; // threshold sig share of quorumHash+validMembers+pubKeyHash+vvecHash
     172             :     CBLSSignature sig; // single member sig of quorumHash+validMembers+pubKeyHash+vvecHash
     173             : 
     174             : public:
     175       39251 :     CDKGPrematureCommitment() = default;
     176        9328 :     explicit CDKGPrematureCommitment(const Consensus::LLMQParams& params) :
     177        6996 :             validMembers((size_t)params.size) {};
     178             : 
     179       24681 :     [[nodiscard]] int CountValidMembers() const
     180             :     {
     181       24681 :         return int(std::count(validMembers.begin(), validMembers.end(), true));
     182             :     }
     183             : 
     184             : public:
     185       67712 :     SERIALIZE_METHODS(CDKGPrematureCommitment, obj)
     186             :     {
     187       22571 :         READWRITE(
     188             :                 obj.llmqType,
     189             :                 obj.quorumHash,
     190             :                 obj.proTxHash,
     191             :                 DYNBITSET(obj.validMembers),
     192             :                 obj.quorumPublicKey,
     193             :                 obj.quorumVvecHash,
     194             :                 obj.quorumSig,
     195             :                 obj.sig
     196             :                 );
     197       22571 :     }
     198             : 
     199             :     [[nodiscard]] uint256 GetSignHash() const;
     200             : };
     201             : 
     202             : class CDKGMember
     203             : {
     204             : public:
     205             :     CDKGMember(const CDeterministicMNCPtr& _dmn, size_t _idx);
     206             : 
     207             :     CDeterministicMNCPtr dmn;
     208             :     size_t idx;
     209             :     CBLSId id;
     210             : 
     211             :     Uint256HashSet contributions;
     212             :     Uint256HashSet complaints;
     213             :     Uint256HashSet justifications;
     214             :     Uint256HashSet prematureCommitments;
     215             : 
     216             :     Uint256HashSet badMemberVotes;
     217             :     Uint256HashSet complaintsFromOthers;
     218             : 
     219             :     bool bad{false};
     220             :     bool badConnection{false};
     221             :     bool weComplain{false};
     222             :     bool someoneComplain{false};
     223             : };
     224             : 
     225             : class DKGError {
     226             : public:
     227             :     enum type {
     228             :         COMPLAIN_LIE = 0,
     229             :         COMMIT_OMIT,
     230             :         COMMIT_LIE,
     231             :         CONTRIBUTION_OMIT,
     232             :         CONTRIBUTION_LIE,
     233             :         JUSTIFY_OMIT,
     234             :         JUSTIFY_LIE,
     235             :         _COUNT
     236             :     };
     237          26 :     static constexpr DKGError::type from_string(std::string_view in) {
     238          26 :         if (in == "complain-lie") return COMPLAIN_LIE;
     239          22 :         if (in == "commit-omit") return COMMIT_OMIT;
     240          18 :         if (in == "commit-lie") return COMMIT_LIE;
     241          16 :         if (in == "contribution-omit") return CONTRIBUTION_OMIT;
     242          12 :         if (in == "contribution-lie") return CONTRIBUTION_LIE;
     243           8 :         if (in == "justify-lie") return JUSTIFY_LIE;
     244           4 :         if (in == "justify-omit") return JUSTIFY_OMIT;
     245           0 :         return _COUNT;
     246          26 :     }
     247             : };
     248             : 
     249             : class CDKGLogger : public CBatchedLogger
     250             : {
     251             : public:
     252             :     CDKGLogger(const CDKGSession& _quorumDkg, std::string_view _func, int source_line);
     253             : };
     254             : 
     255             : /**
     256             :  * The DKG session is a single instance of the DKG process. It is owned and called by CDKGSessionHandler, which passes
     257             :  * received DKG messages to the session. The session is not persistent and will loose it's state (the whole object is
     258             :  * discarded) when it finishes (after the mining phase) or is aborted.
     259             :  *
     260             :  * When incoming contributions are received and the verification vector is valid, it is passed to CDKGSessionManager
     261             :  * which will store it in the evo DB. Secret key contributions which are meant for the local member are also passed
     262             :  * to CDKGSessionManager to store them in the evo DB. If verification of the SK contribution initially fails, it is
     263             :  * not passed to CDKGSessionManager. If the justification phase later gives a valid SK contribution from the same
     264             :  * member, it is then passed to CDKGSessionManager and after this handled the same way.
     265             :  *
     266             :  * The contributions stored by CDKGSessionManager are then later loaded by the quorum instances and used for signing
     267             :  * sessions, but only if the local node is a member of the quorum.
     268             :  */
     269             : class CDKGSession
     270             : {
     271             :     friend class CDKGLogger;
     272             : 
     273             : protected:
     274             :     enum class MsgPhase : uint8_t {
     275             :         Contribution,
     276             :         Complaint,
     277             :         Justification
     278             :     };
     279             : 
     280             :     struct ReceiveMessageState {
     281             :         CDKGMember* member{nullptr};
     282             :         uint256 hash{};
     283             :         CInv inv{};
     284             :         bool should_process{true};
     285             :     };
     286             : 
     287             : protected:
     288             :     CBLSWorker& blsWorker;
     289             :     CBLSWorkerCache cache;
     290             :     CDeterministicMNManager& m_dmnman;
     291             :     CDKGDebugManager& dkgDebugManager;
     292             :     CDKGSessionManager& dkgManager;
     293             :     CQuorumSnapshotManager& m_qsnapman;
     294             :     const ChainstateManager& m_chainman;
     295             :     const Consensus::LLMQParams& params;
     296             :     const CBlockIndex* const m_quorum_base_block_index;
     297             : 
     298             : protected:
     299             :     int quorumIndex{0};
     300             :     std::vector<std::unique_ptr<CDKGMember>> members;
     301             :     std::map<uint256, size_t> membersMap;
     302             :     Uint256HashSet relayMembers;
     303             :     BLSVerificationVectorPtr vvecContribution;
     304             :     std::vector<CBLSSecretKey> m_sk_contributions;
     305             : 
     306             :     std::vector<CBLSId> memberIds;
     307             :     std::vector<BLSVerificationVectorPtr> receivedVvecs;
     308             :     // these are not necessarily verified yet. Only trust in what was written to the DB
     309             :     std::vector<CBLSSecretKey> receivedSkContributions;
     310             :     /// Contains the received unverified/encrypted DKG contributions
     311             :     std::vector<std::shared_ptr<CBLSIESMultiRecipientObjects<CBLSSecretKey>>> vecEncryptedContributions;
     312             : 
     313             :     uint256 myProTxHash;
     314             :     CBLSId myId;
     315             :     std::optional<size_t> myIdx;
     316             : 
     317             :     // all indexed by msg hash
     318             :     // we expect to only receive a single vvec and contribution per member, but we must also be able to relay
     319             :     // conflicting messages as otherwise an attacker might be able to broadcast conflicting (valid+invalid) messages
     320             :     // and thus split the quorum. Such members are later removed from the quorum.
     321             :     mutable Mutex invCs;
     322             :     std::map<uint256, CDKGContribution> contributions GUARDED_BY(invCs);
     323             :     std::map<uint256, CDKGComplaint> complaints GUARDED_BY(invCs);
     324             :     std::map<uint256, CDKGJustification> justifications GUARDED_BY(invCs);
     325             :     std::map<uint256, CDKGPrematureCommitment> prematureCommitments GUARDED_BY(invCs);
     326             : 
     327             :     mutable Mutex cs_pending;
     328             :     std::vector<size_t> pendingContributionVerifications GUARDED_BY(cs_pending);
     329             : 
     330             :     // filled by ReceivePrematureCommitment and used by FinalizeCommitments
     331             :     Uint256HashSet validCommitments GUARDED_BY(invCs);
     332             : 
     333             : public:
     334             :     CDKGSession(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CDKGDebugManager& _dkgDebugManager,
     335             :                 CDKGSessionManager& _dkgManager, CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman,
     336             :                 const CBlockIndex* pQuorumBaseBlockIndex, const Consensus::LLMQParams& _params);
     337             :     virtual ~CDKGSession();
     338             : 
     339             :     // TODO: remove Init completely
     340             :     bool Init(const uint256& _myProTxHash, int _quorumIndex);
     341             : 
     342             :     /**
     343             :      * The following sets of methods are for the first 4 phases handled in the session. The flow of message calls
     344             :      * is identical for all phases:
     345             :      * 1. Execute local action (e.g. create/send own contributions)
     346             :      * 2. PreVerify incoming messages for this phase. Preverification means that everything from the message is checked
     347             :      *    that does not require too much resources for verification. This specifically excludes all CPU intensive BLS
     348             :      *    operations.
     349             :      * 3. CDKGSessionHandler will collect pre verified messages in batches and perform batched BLS signature verification
     350             :      *    on these.
     351             :      * 4. ReceiveMessage is called for each pre verified message with a valid signature. ReceiveMessage is also
     352             :      *    responsible for further verification of validity (e.g. validate vvecs and SK contributions).
     353             :      */
     354             : 
     355             :     // Phase 1: contribution
     356           0 :     virtual std::optional<CDKGContribution> Contribute() { return std::nullopt; }
     357           0 :     virtual std::optional<CDKGContribution> SendContributions() { return std::nullopt; }
     358             :     bool PreVerifyMessage(const CDKGContribution& qc, bool& retBan) const;
     359             :     std::optional<CInv> ReceiveMessage(const CDKGContribution& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs, !cs_pending);
     360           0 :     virtual void VerifyPendingContributions() EXCLUSIVE_LOCKS_REQUIRED(cs_pending) {}
     361             : 
     362             :     // Phase 2: complaint
     363           0 :     virtual std::optional<CDKGComplaint> VerifyAndComplain(CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_pending)
     364             :     {
     365           0 :         return std::nullopt;
     366             :     }
     367           0 :     virtual void VerifyConnectionAndMinProtoVersions(CConnman& connman) const {}
     368           0 :     virtual std::optional<CDKGComplaint> SendComplaint() { return std::nullopt; }
     369             :     bool PreVerifyMessage(const CDKGComplaint& qc, bool& retBan) const;
     370             :     std::optional<CInv> ReceiveMessage(const CDKGComplaint& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     371             : 
     372             :     // Phase 3: justification
     373           0 :     virtual std::optional<CDKGJustification> VerifyAndJustify() EXCLUSIVE_LOCKS_REQUIRED(!invCs)
     374             :     {
     375           0 :         return std::nullopt;
     376             :     }
     377           0 :     virtual std::optional<CDKGJustification> SendJustification(const Uint256HashSet& forMembers)
     378             :     {
     379           0 :         return std::nullopt;
     380             :     }
     381             :     bool PreVerifyMessage(const CDKGJustification& qj, bool& retBan) const;
     382             :     std::optional<CInv> ReceiveMessage(const CDKGJustification& qj) EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     383             : 
     384             :     // Phase 4: commit
     385           0 :     virtual std::optional<CDKGPrematureCommitment> VerifyAndCommit() { return std::nullopt; }
     386           0 :     virtual std::optional<CDKGPrematureCommitment> SendCommitment() { return std::nullopt; }
     387             :     bool PreVerifyMessage(const CDKGPrematureCommitment& qc, bool& retBan) const;
     388             :     std::optional<CInv> ReceiveMessage(const CDKGPrematureCommitment& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     389             : 
     390             :     // Phase 5: aggregate/finalize
     391             :     virtual std::vector<CFinalCommitment> FinalizeCommitments() EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     392             : 
     393             :     // All Phases 5-in-1 for single-node-quorum
     394             :     virtual CFinalCommitment FinalizeSingleCommitment();
     395             : 
     396             :     //! Look up a received message by hash. Used by CDKGSessionHandler subclasses to implement their Get* virtuals.
     397             :     [[nodiscard]] bool GetContribution(const uint256& hash, CDKGContribution& ret) const EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     398             :     [[nodiscard]] bool GetComplaint(const uint256& hash, CDKGComplaint& ret) const EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     399             :     [[nodiscard]] bool GetJustification(const uint256& hash, CDKGJustification& ret) const EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     400             :     [[nodiscard]] bool GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const EXCLUSIVE_LOCKS_REQUIRED(!invCs);
     401             : 
     402             : public:
     403      146215 :     [[nodiscard]] bool AreWeMember() const { return !myProTxHash.IsNull(); }
     404             :     [[nodiscard]] CDKGMember* GetMember(const uint256& proTxHash) const;
     405             :     [[nodiscard]] CDKGMember* GetMemberAtIndex(size_t index) const;
     406           0 :     [[nodiscard]] std::optional<size_t> GetMyMemberIndex() const { return myIdx; }
     407       17960 :     [[nodiscard]] const Uint256HashSet& RelayMembers() const { return relayMembers; }
     408       35920 :     [[nodiscard]] const CBlockIndex* BlockIndex() const { return m_quorum_base_block_index; }
     409       43733 :     [[nodiscard]] const uint256& ProTx() const { return myProTxHash; }
     410       17960 :     [[nodiscard]] Consensus::LLMQType GetType() const { return params.type; }
     411             : 
     412             : protected:
     413           0 :     virtual bool MaybeDecrypt(const CBLSIESMultiRecipientObjects<CBLSSecretKey>& obj, size_t idx,
     414             :                               CBLSSecretKey& ret_obj, int version)
     415             :     {
     416           0 :         return false;
     417             :     }
     418             : 
     419             :     [[nodiscard]] bool ShouldSimulateError(DKGError::type type) const;
     420             : 
     421             :     template <typename MsgType>
     422             :     [[nodiscard]] std::optional<ReceiveMessageState> ReceiveMessagePreamble(const MsgType& msg, MsgPhase phase, CDKGLogger& logger)
     423             :         EXCLUSIVE_LOCKS_REQUIRED(invCs);
     424             : 
     425             :     void MarkBadMember(size_t idx);
     426             : };
     427             : 
     428             : void SetSimulatedDKGErrorRate(DKGError::type type, double rate);
     429             : double GetSimulatedErrorRate(DKGError::type type);
     430             : } // namespace llmq
     431             : 
     432             : #endif // BITCOIN_LLMQ_DKGSESSION_H

Generated by: LCOV version 1.16