Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <llmq/dkgsessionmgr.h>
6 :
7 : #include <bls/bls_ies.h>
8 : #include <evo/deterministicmns.h>
9 : #include <llmq/dkgsessionhandler.h>
10 : #include <llmq/options.h>
11 : #include <llmq/utils.h>
12 : #include <msg_result.h>
13 : #include <spork.h>
14 : #include <unordered_lru_cache.h>
15 : #include <util/helpers.h>
16 : #include <util/std23.h>
17 :
18 : #include <chainparams.h>
19 : #include <dbwrapper.h>
20 : #include <deploymentstatus.h>
21 : #include <validation.h>
22 :
23 : namespace llmq
24 : {
25 : static const std::string DB_VVEC = "qdkg_V";
26 : static const std::string DB_SKCONTRIB = "qdkg_S";
27 : static const std::string DB_ENC_CONTRIB = "qdkg_E";
28 :
29 1998 : CDKGSessionManager::CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman,
30 : const ChainstateManager& chainman, const CSporkManager& sporkman,
31 : const util::DbWrapperParams& db_params) :
32 666 : m_dmnman{dmnman},
33 666 : m_qsnapman{qsnapman},
34 666 : m_chainman{chainman},
35 666 : m_sporkman{sporkman},
36 666 : db{util::MakeDbWrapper({db_params.path / "llmq" / "dkgdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}
37 666 : {
38 1332 : }
39 :
40 1332 : CDKGSessionManager::~CDKGSessionManager() = default;
41 :
42 81279 : void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload)
43 : {
44 81279 : CleanupCache();
45 :
46 81279 : if (fInitialDownload)
47 0 : return;
48 81279 : if (!DeploymentDIP0003Enforced(pindexNew->nHeight, Params().GetConsensus()))
49 7164 : return;
50 74115 : if (!IsQuorumDKGEnabled(m_sporkman))
51 17123 : return;
52 :
53 398944 : for (auto& [_, dkgType] : dkgSessionHandlers) {
54 341952 : Assert(dkgType)->UpdatedBlockTip(pindexNew);
55 : }
56 81279 : }
57 :
58 6792 : bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& ret) const
59 : {
60 6792 : if (!IsQuorumDKGEnabled(m_sporkman))
61 0 : return false;
62 :
63 24507 : for (const auto& [_, dkgType] : dkgSessionHandlers) {
64 17715 : const auto dkgPhase = Assert(dkgType)->GetPhase();
65 17715 : if (dkgPhase < QuorumPhase::Initialized || dkgPhase > QuorumPhase::Contribute) {
66 4678 : continue;
67 : }
68 13037 : if (dkgType->GetContribution(hash, ret)) {
69 6518 : return true;
70 : }
71 : }
72 274 : return false;
73 6792 : }
74 :
75 1214 : bool CDKGSessionManager::GetComplaint(const uint256& hash, CDKGComplaint& ret) const
76 : {
77 1214 : if (!IsQuorumDKGEnabled(m_sporkman))
78 7 : return false;
79 :
80 5047 : for (const auto& [_, dkgType] : dkgSessionHandlers) {
81 3840 : const auto dkgPhase = Assert(dkgType)->GetPhase();
82 3840 : if (dkgPhase < QuorumPhase::Contribute || dkgPhase > QuorumPhase::Complain) {
83 2211 : continue;
84 : }
85 1629 : if (dkgType->GetComplaint(hash, ret)) {
86 1057 : return true;
87 : }
88 : }
89 150 : return false;
90 1214 : }
91 :
92 16 : bool CDKGSessionManager::GetJustification(const uint256& hash, CDKGJustification& ret) const
93 : {
94 16 : if (!IsQuorumDKGEnabled(m_sporkman))
95 0 : return false;
96 :
97 32 : for (const auto& [_, dkgType] : dkgSessionHandlers) {
98 16 : const auto dkgPhase = Assert(dkgType)->GetPhase();
99 16 : if (dkgPhase < QuorumPhase::Complain || dkgPhase > QuorumPhase::Justify) {
100 0 : continue;
101 : }
102 16 : if (dkgType->GetJustification(hash, ret)) {
103 16 : return true;
104 : }
105 : }
106 0 : return false;
107 16 : }
108 :
109 5579 : bool CDKGSessionManager::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const
110 : {
111 5579 : if (!IsQuorumDKGEnabled(m_sporkman))
112 0 : return false;
113 :
114 18715 : for (const auto& [_, dkgType] : dkgSessionHandlers) {
115 13136 : const auto dkgPhase = Assert(dkgType)->GetPhase();
116 13136 : if (dkgPhase < QuorumPhase::Justify || dkgPhase > QuorumPhase::Commit) {
117 1901 : continue;
118 : }
119 11235 : if (dkgType->GetPrematureCommitment(hash, ret)) {
120 5548 : return true;
121 : }
122 : }
123 31 : return false;
124 5579 : }
125 :
126 8948 : void CDKGSessionManager::WriteVerifiedVvecContribution(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const BLSVerificationVectorPtr& vvec)
127 : {
128 8948 : CDataStream s(SER_DISK, CLIENT_VERSION);
129 8948 : WriteCompactSize(s, vvec->size());
130 34204 : for (auto& pubkey : *vvec) {
131 25256 : s << CBLSPublicKeyVersionWrapper(pubkey, false);
132 : }
133 8948 : db->Write(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), s);
134 8948 : }
135 :
136 8645 : void CDKGSessionManager::WriteVerifiedSkContribution(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSSecretKey& skContribution)
137 : {
138 8645 : db->Write(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution);
139 8645 : }
140 :
141 8649 : void CDKGSessionManager::WriteEncryptedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSIESMultiRecipientObjects<CBLSSecretKey>& contributions)
142 : {
143 8649 : db->Write(std::make_tuple(DB_ENC_CONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), contributions);
144 8649 : }
145 :
146 13213 : bool CDKGSessionManager::GetVerifiedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const std::vector<bool>& validMembers, std::vector<uint16_t>& memberIndexesRet, std::vector<BLSVerificationVectorPtr>& vvecsRet, std::vector<CBLSSecretKey>& skContributionsRet) const
147 : {
148 13213 : auto members = utils::GetAllQuorumMembers(llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
149 :
150 13213 : memberIndexesRet.clear();
151 13213 : vvecsRet.clear();
152 13213 : skContributionsRet.clear();
153 13213 : memberIndexesRet.reserve(members.size());
154 13213 : vvecsRet.reserve(members.size());
155 13213 : skContributionsRet.reserve(members.size());
156 :
157 : // NOTE: the `cs_main` should not be locked under scope of `contributionsCacheCs`
158 13213 : LOCK(contributionsCacheCs);
159 57495 : for (const auto i : util::irange(members.size())) {
160 46237 : if (validMembers[i]) {
161 45416 : const uint256& proTxHash = members[i]->proTxHash;
162 45416 : ContributionsCacheKey cacheKey = {llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash};
163 45416 : auto it = contributionsCache.find(cacheKey);
164 45416 : if (it == contributionsCache.end()) {
165 9606 : CDataStream s(SER_DISK, CLIENT_VERSION);
166 9606 : if (!db->ReadDataStream(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), s)) {
167 1955 : LogPrint(BCLog::LLMQ, "%s -- this node does not have vvec for llmq=%d block=%s protx=%s\n",
168 : __func__, std23::to_underlying(llmqType), pQuorumBaseBlockIndex->GetBlockHash().ToString(),
169 : proTxHash.ToString());
170 1955 : return false;
171 : }
172 7651 : size_t vvec_size = ReadCompactSize(s);
173 7651 : CBLSPublicKey pubkey;
174 7651 : std::vector<CBLSPublicKey> qv;
175 29165 : for ([[maybe_unused]] size_t _ : util::irange(vvec_size)) {
176 21514 : s >> CBLSPublicKeyVersionWrapper(pubkey, false);
177 21514 : qv.emplace_back(pubkey);
178 : }
179 7651 : auto vvecPtr = std::make_shared<std::vector<CBLSPublicKey>>(std::move(qv));
180 :
181 7651 : CBLSSecretKey skContribution;
182 7651 : db->Read(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution);
183 :
184 7651 : it = contributionsCache.emplace(cacheKey, ContributionsCacheEntry{SteadyClock::now(), vvecPtr, skContribution}).first;
185 9606 : }
186 :
187 43461 : memberIndexesRet.emplace_back(i);
188 43461 : vvecsRet.emplace_back(it->second.vvec);
189 43461 : skContributionsRet.emplace_back(it->second.skContribution);
190 43461 : }
191 : }
192 11258 : return true;
193 13213 : }
194 :
195 81 : bool CDKGSessionManager::GetEncryptedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const std::vector<bool>& validMembers, const uint256& nProTxHash, std::vector<CBLSIESEncryptedObject<CBLSSecretKey>>& vecRet) const
196 : {
197 81 : auto members = utils::GetAllQuorumMembers(llmqType, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex});
198 :
199 81 : vecRet.clear();
200 81 : vecRet.reserve(members.size());
201 :
202 81 : size_t nRequestedMemberIdx{std::numeric_limits<size_t>::max()};
203 187 : for (const auto i : util::irange(members.size())) {
204 : // cppcheck-suppress useStlAlgorithm
205 187 : if (members[i]->proTxHash == nProTxHash) {
206 81 : nRequestedMemberIdx = i;
207 81 : break;
208 : }
209 : }
210 81 : if (nRequestedMemberIdx == std::numeric_limits<size_t>::max()) {
211 0 : LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- not a member, nProTxHash=%s\n", __func__, nProTxHash.ToString());
212 0 : return false;
213 : }
214 :
215 299 : for (const auto i : util::irange(members.size())) {
216 232 : if (validMembers[i]) {
217 231 : CBLSIESMultiRecipientObjects<CBLSSecretKey> encryptedContributions;
218 231 : if (!db->Read(std::make_tuple(DB_ENC_CONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), members[i]->proTxHash), encryptedContributions)) {
219 14 : LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- can't read from db, nProTxHash=%s\n", __func__, nProTxHash.ToString());
220 14 : return false;
221 : }
222 217 : vecRet.emplace_back(encryptedContributions.Get(nRequestedMemberIdx));
223 231 : }
224 : }
225 67 : return true;
226 81 : }
227 :
228 81279 : void CDKGSessionManager::CleanupCache() const
229 : {
230 81279 : LOCK(contributionsCacheCs);
231 81279 : const auto curTime = SteadyClock::now();
232 592754 : for (auto it = contributionsCache.begin(); it != contributionsCache.end(); ) {
233 511475 : if (curTime - it->second.entryTime > MAX_CONTRIBUTION_CACHE_TIME) {
234 1510 : it = contributionsCache.erase(it);
235 1510 : } else {
236 509965 : ++it;
237 : }
238 : }
239 81279 : }
240 :
241 10551 : void CDKGSessionManager::CleanupOldContributions() const
242 : {
243 10551 : if (db->IsEmpty()) {
244 57 : return;
245 : }
246 :
247 10494 : const auto prefixes = {DB_VVEC, DB_SKCONTRIB, DB_ENC_CONTRIB};
248 :
249 11334 : for (const auto& params : Params().GetConsensus().llmqs) {
250 840 : LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- looking for old entries for llmq type %d\n", __func__, std23::to_underlying(params.type));
251 :
252 840 : CDBBatch batch(*db);
253 11166 : size_t cnt_old{0}, cnt_all{0};
254 13686 : for (const auto& prefix : prefixes) {
255 12846 : std::unique_ptr<CDBIterator> pcursor(db->NewIterator());
256 12846 : auto start = std::make_tuple(prefix, params.type, uint256(), uint256());
257 12846 : decltype(start) k;
258 :
259 12846 : pcursor->Seek(start);
260 2520 : LOCK(::cs_main);
261 19944 : while (pcursor->Valid()) {
262 9144 : if (!pcursor->GetKey(k) || std::get<0>(k) != prefix || std::get<1>(k) != params.type) {
263 2046 : break;
264 : }
265 7098 : cnt_all++;
266 7098 : const CBlockIndex* pindexQuorum = m_chainman.m_blockman.LookupBlockIndex(std::get<2>(k));
267 14196 : if (pindexQuorum == nullptr ||
268 7098 : m_chainman.ActiveHeight() - pindexQuorum->nHeight > params.max_store_depth()) {
269 : // not found or too old
270 7098 : batch.Erase(k);
271 1935 : cnt_old++;
272 1935 : }
273 1935 : pcursor->Next();
274 : }
275 2520 : pcursor.reset();
276 23172 : }
277 840 : LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- found %lld entries for llmq type %d\n", __func__, cnt_all, uint8_t(params.type));
278 840 : if (cnt_old > 0) {
279 74 : db->WriteBatch(batch);
280 74 : LogPrint(BCLog::LLMQ, "CDKGSessionManager::%s -- removed %lld old entries for llmq type %d\n", __func__, cnt_old, uint8_t(params.type));
281 74 : }
282 840 : }
283 103485 : }
284 : } // namespace llmq
|