Line data Source code
1 : // Copyright (c) 2018-2022 The Dash Core developers
2 : // Copyright (c) 2023 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 : #ifndef PIVX_LLMQ_QUORUMS_SIGNING_H
7 : #define PIVX_LLMQ_QUORUMS_SIGNING_H
8 :
9 : #include "llmq/quorums.h"
10 :
11 : #include "chainparams.h"
12 : #include "net.h"
13 : #include "saltedhasher.h"
14 : #include "sync.h"
15 : #include "unordered_lru_cache.h"
16 :
17 : #include <unordered_map>
18 :
19 : namespace llmq
20 : {
21 :
22 14082 : class CRecoveredSig
23 : {
24 : public:
25 : uint8_t llmqType;
26 : uint256 quorumHash;
27 : uint256 id;
28 : uint256 msgHash;
29 : CBLSLazySignature sig;
30 :
31 : // only in-memory
32 : uint256 hash;
33 :
34 : public:
35 19844 : SERIALIZE_METHODS(CRecoveredSig, obj)
36 : {
37 10754 : READWRITE(obj.llmqType);
38 10754 : READWRITE(obj.quorumHash);
39 10754 : READWRITE(obj.id);
40 10754 : READWRITE(obj.msgHash);
41 10754 : READWRITE(obj.sig);
42 14082 : SER_READ(obj, obj.UpdateHash());
43 10754 : }
44 :
45 3713 : void UpdateHash()
46 : {
47 3713 : hash = ::SerializeHash(*this);
48 385 : }
49 :
50 16901 : const uint256& GetHash() const
51 : {
52 33802 : assert(!hash.IsNull());
53 16901 : return hash;
54 : }
55 : };
56 :
57 : class CRecoveredSigsDb
58 : {
59 : private:
60 : CDBWrapper& db;
61 :
62 : RecursiveMutex cs;
63 : unordered_lru_cache<std::pair<Consensus::LLMQType, uint256>, bool, StaticSaltedHasher, 30000> hasSigForIdCache;
64 : unordered_lru_cache<uint256, bool, StaticSaltedHasher, 30000> hasSigForSessionCache;
65 : unordered_lru_cache<uint256, bool, StaticSaltedHasher, 30000> hasSigForHashCache;
66 :
67 : public:
68 : explicit CRecoveredSigsDb(CDBWrapper& _db);
69 :
70 : void ConvertInvalidTimeKeys();
71 : void AddVoteTimeKeys();
72 :
73 : bool HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
74 : bool HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id);
75 : bool HasRecoveredSigForSession(const uint256& signHash);
76 : bool HasRecoveredSigForHash(const uint256& hash);
77 : bool GetRecoveredSigByHash(const uint256& hash, CRecoveredSig& ret);
78 : bool GetRecoveredSigById(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret);
79 : void WriteRecoveredSig(const CRecoveredSig& recSig);
80 : void RemoveRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);
81 : void TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);
82 :
83 : void CleanupOldRecoveredSigs(int64_t maxAge);
84 :
85 : // votes are removed when the recovered sig is written to the db
86 : bool HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id);
87 : bool GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet);
88 : void WriteVoteForId(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
89 :
90 : void CleanupOldVotes(int64_t maxAge);
91 :
92 : private:
93 : bool ReadRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret);
94 : void RemoveRecoveredSig(CDBBatch& batch, Consensus::LLMQType llmqType, const uint256& id, bool deleteHashKey, bool deleteTimeKey);
95 : };
96 :
97 950 : class CRecoveredSigsListener
98 : {
99 : public:
100 950 : virtual ~CRecoveredSigsListener() {}
101 :
102 : virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig) = 0;
103 : };
104 :
105 : class CSigningManager
106 : {
107 : friend class CSigSharesManager;
108 : static const int64_t DEFAULT_MAX_RECOVERED_SIGS_AGE = 60 * 60 * 24 * 7; // keep them for a week
109 :
110 : // when selecting a quorum for signing and verification, we use CQuorumManager::SelectQuorum with this offset as
111 : // starting height for scanning. This is because otherwise the resulting signatures would not be verifiable by nodes
112 : // which are not 100% at the chain tip.
113 : static const int SIGN_HEIGHT_OFFSET = 8;
114 :
115 : private:
116 : RecursiveMutex cs;
117 :
118 : CRecoveredSigsDb db;
119 :
120 : // Incoming and not verified yet
121 : std::unordered_map<NodeId, std::list<CRecoveredSig>> pendingRecoveredSigs;
122 :
123 : // must be protected by cs
124 : FastRandomContext rnd;
125 :
126 : int64_t lastCleanupTime{0};
127 :
128 : std::vector<CRecoveredSigsListener*> recoveredSigsListeners;
129 :
130 : public:
131 : CSigningManager(CDBWrapper& llmqDb, bool fMemory);
132 :
133 : bool AlreadyHave(const CInv& inv);
134 : bool GetRecoveredSigForGetData(const uint256& hash, CRecoveredSig& ret);
135 :
136 : void ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
137 :
138 : // This is called when a recovered signature can be safely removed from the DB. This is only safe when some other
139 : // mechanism prevents possible conflicts. As an example, ChainLocks prevent conflicts in confirmed TXs InstantSend votes
140 : // This won't completely remove all traces of the recovered sig but instead leave the hash entry in the DB. This
141 : // allows AlreadyHave to keep returning true. Cleanup will later remove the remains
142 : void TruncateRecoveredSig(Consensus::LLMQType llmqType, const uint256& id);
143 :
144 : private:
145 : void ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman);
146 : bool PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan);
147 :
148 : void CollectPendingRecoveredSigsToVerify(size_t maxUniqueSessions,
149 : std::unordered_map<NodeId, std::list<CRecoveredSig>>& retSigShares,
150 : std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums);
151 : bool ProcessPendingRecoveredSigs(CConnman& connman); // called from the worker thread of CSigSharesManager
152 : void ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum, CConnman& connman);
153 : void Cleanup(); // called from the worker thread of CSigSharesManager
154 :
155 : public:
156 : // public interface
157 : void RegisterRecoveredSigsListener(CRecoveredSigsListener* l);
158 : void UnregisterRecoveredSigsListener(CRecoveredSigsListener* l);
159 : bool AsyncSignIfMember(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash, bool allowReSign = false);
160 : bool HasRecoveredSig(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
161 : bool HasRecoveredSigForId(Consensus::LLMQType llmqType, const uint256& id);
162 : bool HasRecoveredSigForSession(const uint256& signHash);
163 : bool IsConflicting(Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
164 : bool HasVotedOnId(Consensus::LLMQType llmqType, const uint256& id);
165 : bool GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet);
166 :
167 : std::vector<CQuorumCPtr> GetActiveQuorumSet(Consensus::LLMQType llmqType, int signHeight);
168 : CQuorumCPtr SelectQuorumForSigning(Consensus::LLMQType llmqType, const uint256& selectionHash, int signHeight = -1 /*chain tip*/, int signOffset = SIGN_HEIGHT_OFFSET);
169 : // Verifies a recovered sig that was signed while the chain tip was at signedAtTip
170 : bool VerifyRecoveredSig(Consensus::LLMQType llmqType, int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig);
171 : };
172 :
173 : extern std::unique_ptr<CSigningManager> quorumSigningManager;
174 :
175 : } // namespace llmq
176 :
177 : #endif // PIVX_LLMQ_QUORUMS_SIGNING_H
|