Line data Source code
1 : // Copyright (c) 2018-2021 The Dash Core developers
2 : // Copyright (c) 2021-2022 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "evo/deterministicmns.h"
7 :
8 : #include "bls/key_io.h"
9 : #include "chain.h"
10 : #include "coins.h"
11 : #include "chainparams.h"
12 : #include "consensus/upgrades.h"
13 : #include "consensus/validation.h"
14 : #include "core_io.h"
15 : #include "key_io.h"
16 : #include "guiinterface.h"
17 : #include "masternodeman.h" // for mnodeman (!TODO: remove)
18 : #include "script/standard.h"
19 : #include "spork.h"
20 : #include "sync.h"
21 :
22 : #include <univalue.h>
23 :
24 : static const std::string DB_LIST_SNAPSHOT = "dmn_S";
25 : static const std::string DB_LIST_DIFF = "dmn_D";
26 :
27 : std::unique_ptr<CDeterministicMNManager> deterministicMNManager;
28 :
29 51 : std::string CDeterministicMNState::ToString() const
30 : {
31 51 : CTxDestination dest;
32 102 : std::string payoutAddress = "unknown";
33 102 : std::string operatorPayoutAddress = "none";
34 51 : if (ExtractDestination(scriptPayout, dest)) {
35 51 : payoutAddress = EncodeDestination(dest);
36 : }
37 51 : if (ExtractDestination(scriptOperatorPayout, dest)) {
38 1 : operatorPayoutAddress = EncodeDestination(dest);
39 : }
40 :
41 51 : return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, ownerAddress=%s, operatorPubKey=%s, votingAddress=%s, addr=%s, payoutAddress=%s, operatorPayoutAddress=%s)",
42 51 : nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
43 255 : EncodeDestination(keyIDOwner), bls::EncodePublic(Params(), pubKeyOperator.Get()), EncodeDestination(keyIDVoting), addr.ToStringIPPort(), payoutAddress, operatorPayoutAddress);
44 : }
45 :
46 1272 : void CDeterministicMNState::ToJson(UniValue& obj) const
47 : {
48 1272 : obj.clear();
49 1272 : obj.setObject();
50 2544 : obj.pushKV("service", addr.ToStringIPPort());
51 1272 : obj.pushKV("registeredHeight", nRegisteredHeight);
52 1272 : obj.pushKV("lastPaidHeight", nLastPaidHeight);
53 1272 : obj.pushKV("PoSePenalty", nPoSePenalty);
54 1272 : obj.pushKV("PoSeRevivedHeight", nPoSeRevivedHeight);
55 1272 : obj.pushKV("PoSeBanHeight", nPoSeBanHeight);
56 1272 : obj.pushKV("revocationReason", nRevocationReason);
57 3816 : obj.pushKV("ownerAddress", EncodeDestination(keyIDOwner));
58 2544 : obj.pushKV("operatorPubKey", bls::EncodePublic(Params(), pubKeyOperator.Get()));
59 3816 : obj.pushKV("votingAddress", EncodeDestination(keyIDVoting));
60 :
61 1272 : CTxDestination dest1;
62 1272 : if (ExtractDestination(scriptPayout, dest1)) {
63 3816 : obj.pushKV("payoutAddress", EncodeDestination(dest1));
64 : }
65 2544 : CTxDestination dest2;
66 1272 : if (ExtractDestination(scriptOperatorPayout, dest2)) {
67 246 : obj.pushKV("operatorPayoutAddress", EncodeDestination(dest2));
68 : }
69 1272 : }
70 :
71 31820 : uint64_t CDeterministicMN::GetInternalId() const
72 : {
73 : // can't get it if it wasn't set yet
74 31820 : assert(internalId != std::numeric_limits<uint64_t>::max());
75 31820 : return internalId;
76 : }
77 :
78 51 : std::string CDeterministicMN::ToString() const
79 : {
80 204 : return strprintf("CDeterministicMN(proTxHash=%s, collateralOutpoint=%s, nOperatorReward=%f, state=%s", proTxHash.ToString(), collateralOutpoint.ToStringShort(), (double)nOperatorReward / 100, pdmnState->ToString());
81 : }
82 :
83 1272 : void CDeterministicMN::ToJson(UniValue& obj) const
84 : {
85 1272 : obj.clear();
86 1272 : obj.setObject();
87 :
88 1272 : UniValue stateObj;
89 1272 : pdmnState->ToJson(stateObj);
90 :
91 2544 : obj.pushKV("proTxHash", proTxHash.ToString());
92 2544 : obj.pushKV("collateralHash", collateralOutpoint.hash.ToString());
93 1272 : obj.pushKV("collateralIndex", (int)collateralOutpoint.n);
94 1272 : obj.pushKV("operatorReward", (double)nOperatorReward / 100);
95 2544 : obj.pushKV("dmnstate", stateObj);
96 1272 : }
97 :
98 151682 : CDeterministicMNCPtr CDeterministicMNList::GetMN(const uint256& proTxHash) const
99 : {
100 151682 : auto p = mnMap.find(proTxHash);
101 151682 : if (p == nullptr) {
102 458 : return nullptr;
103 : }
104 151224 : return *p;
105 : }
106 :
107 6717 : CDeterministicMNCPtr CDeterministicMNList::GetValidMN(const uint256& proTxHash) const
108 : {
109 6717 : auto dmn = GetMN(proTxHash);
110 6717 : if (dmn && dmn->IsPoSeBanned()) {
111 245 : return nullptr;
112 : }
113 6717 : return dmn;
114 : }
115 :
116 301 : CDeterministicMNCPtr CDeterministicMNList::GetMNByOperatorKey(const CBLSPublicKey& pubKey)
117 : {
118 1222 : for (const auto& p : mnMap) {
119 1260 : if (p.second->pdmnState->pubKeyOperator.Get() == pubKey) {
120 91 : return p.second;
121 : }
122 : }
123 210 : return nullptr;
124 : }
125 :
126 534685 : CDeterministicMNCPtr CDeterministicMNList::GetMNByCollateral(const COutPoint& collateralOutpoint) const
127 : {
128 534685 : return GetUniquePropertyMN(collateralOutpoint);
129 : }
130 :
131 0 : CDeterministicMNCPtr CDeterministicMNList::GetValidMNByCollateral(const COutPoint& collateralOutpoint) const
132 : {
133 0 : auto dmn = GetMNByCollateral(collateralOutpoint);
134 0 : if (dmn && dmn->IsPoSeBanned()) {
135 0 : return nullptr;
136 : }
137 0 : return dmn;
138 : }
139 :
140 0 : CDeterministicMNCPtr CDeterministicMNList::GetMNByService(const CService& service) const
141 : {
142 0 : return GetUniquePropertyMN(service);
143 : }
144 :
145 10784 : CDeterministicMNCPtr CDeterministicMNList::GetMNByInternalId(uint64_t internalId) const
146 : {
147 10784 : auto proTxHash = mnInternalIdMap.find(internalId);
148 10784 : if (!proTxHash) {
149 0 : return nullptr;
150 : }
151 10784 : return GetMN(*proTxHash);
152 : }
153 :
154 154728 : static int CompareByLastPaidGetHeight(const CDeterministicMN& dmn)
155 : {
156 154728 : int height = dmn.pdmnState->nLastPaidHeight;
157 154728 : if (dmn.pdmnState->nPoSeRevivedHeight != -1 && dmn.pdmnState->nPoSeRevivedHeight > height) {
158 154728 : height = dmn.pdmnState->nPoSeRevivedHeight;
159 154526 : } else if (height == 0) {
160 6494 : height = dmn.pdmnState->nRegisteredHeight;
161 : }
162 154728 : return height;
163 : }
164 :
165 77364 : static bool CompareByLastPaid(const CDeterministicMN& _a, const CDeterministicMN& _b)
166 : {
167 77364 : int ah = CompareByLastPaidGetHeight(_a);
168 77364 : int bh = CompareByLastPaidGetHeight(_b);
169 77364 : if (ah == bh) {
170 1193 : return _a.proTxHash < _b.proTxHash;
171 : } else {
172 76171 : return ah < bh;
173 : }
174 : }
175 77364 : static bool CompareByLastPaid(const CDeterministicMNCPtr& _a, const CDeterministicMNCPtr& _b)
176 : {
177 77364 : return CompareByLastPaid(*_a, *_b);
178 : }
179 :
180 18308 : CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const
181 : {
182 18308 : if (mnMap.size() == 0) {
183 1861 : return nullptr;
184 : }
185 :
186 34755 : CDeterministicMNCPtr best;
187 16447 : ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
188 93811 : if (!best || CompareByLastPaid(dmn, best)) {
189 44341 : best = dmn;
190 : }
191 93811 : });
192 :
193 16447 : return best;
194 : }
195 :
196 0 : std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(unsigned int nCount) const
197 : {
198 0 : if (nCount > GetValidMNsCount()) {
199 0 : nCount = GetValidMNsCount();
200 : }
201 :
202 0 : std::vector<CDeterministicMNCPtr> result;
203 0 : result.reserve(nCount);
204 :
205 0 : ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
206 0 : result.emplace_back(dmn);
207 0 : });
208 0 : std::sort(result.begin(), result.end(), [&](const CDeterministicMNCPtr& a, const CDeterministicMNCPtr& b) {
209 0 : return CompareByLastPaid(a, b);
210 : });
211 :
212 0 : result.resize(nCount);
213 :
214 0 : return result;
215 : }
216 :
217 4869 : std::vector<CDeterministicMNCPtr> CDeterministicMNList::CalculateQuorum(size_t maxSize, const uint256& modifier) const
218 : {
219 4869 : auto scores = CalculateScores(modifier);
220 :
221 : // sort is descending order
222 4869 : std::sort(scores.rbegin(), scores.rend(), [](const std::pair<arith_uint256, CDeterministicMNCPtr>& a, std::pair<arith_uint256, CDeterministicMNCPtr>& b) {
223 50905 : if (a.first == b.first) {
224 : // this should actually never happen, but we should stay compatible with how the non deterministic MNs did the sorting
225 0 : return a.second->collateralOutpoint < b.second->collateralOutpoint;
226 : }
227 50905 : return a.first < b.first;
228 : });
229 :
230 : // take top maxSize entries and return it
231 4869 : std::vector<CDeterministicMNCPtr> result;
232 4880 : result.resize(std::min(maxSize, scores.size()));
233 19447 : for (size_t i = 0; i < result.size(); i++) {
234 14578 : result[i] = std::move(scores[i].second);
235 : }
236 4869 : return result;
237 : }
238 :
239 4869 : std::vector<std::pair<arith_uint256, CDeterministicMNCPtr>> CDeterministicMNList::CalculateScores(const uint256& modifier) const
240 : {
241 4869 : std::vector<std::pair<arith_uint256, CDeterministicMNCPtr>> scores;
242 4869 : scores.reserve(GetAllMNsCount());
243 4869 : ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
244 57064 : if (dmn->pdmnState->confirmedHash.IsNull()) {
245 : // we only take confirmed MNs into account to avoid hash grinding on the ProRegTxHash to sneak MNs into a
246 : // future quorums
247 194 : return;
248 : }
249 : // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN
250 : // Please note that this is not a double-sha256 but a single-sha256
251 : // The first part is already precalculated (confirmedHashWithProRegTxHash)
252 : // TODO When https://github.com/bitcoin/bitcoin/pull/13191 gets backported, implement something that is similar but for single-sha256
253 28338 : uint256 h;
254 28338 : CSHA256 sha256;
255 28338 : sha256.Write(dmn->pdmnState->confirmedHashWithProRegTxHash.begin(), dmn->pdmnState->confirmedHashWithProRegTxHash.size());
256 28338 : sha256.Write(modifier.begin(), modifier.size());
257 28338 : sha256.Finalize(h.begin());
258 :
259 28338 : scores.emplace_back(UintToArith256(h), dmn);
260 : });
261 :
262 4869 : return scores;
263 : }
264 :
265 106 : int CDeterministicMNList::CalcMaxPoSePenalty() const
266 : {
267 : // Maximum PoSe penalty is dynamic and equals the number of registered MNs
268 : // It's however at least 100.
269 : // This means that the max penalty is usually equal to a full payment cycle
270 106 : return std::max(100, (int)GetAllMNsCount());
271 : }
272 :
273 53 : int CDeterministicMNList::CalcPenalty(int percent) const
274 : {
275 53 : assert(percent > 0);
276 53 : return (CalcMaxPoSePenalty() * percent) / 100;
277 : }
278 :
279 53 : void CDeterministicMNList::PoSePunish(const uint256& proTxHash, int penalty, bool debugLogs)
280 : {
281 53 : assert(penalty > 0);
282 :
283 53 : auto dmn = GetMN(proTxHash);
284 53 : if (!dmn) {
285 0 : throw(std::runtime_error(strprintf("%s: Can't find a masternode with proTxHash=%s", __func__, proTxHash.ToString())));
286 : }
287 :
288 53 : int maxPenalty = CalcMaxPoSePenalty();
289 :
290 106 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
291 53 : newState->nPoSePenalty += penalty;
292 98 : newState->nPoSePenalty = std::min(maxPenalty, newState->nPoSePenalty);
293 :
294 53 : if (debugLogs) {
295 53 : LogPrintf("CDeterministicMNList::%s -- punished MN %s, penalty %d->%d (max=%d)\n",
296 106 : __func__, proTxHash.ToString(), dmn->pdmnState->nPoSePenalty, newState->nPoSePenalty, maxPenalty);
297 : }
298 :
299 53 : if (newState->nPoSePenalty >= maxPenalty && newState->nPoSeBanHeight == -1) {
300 8 : newState->nPoSeBanHeight = nHeight;
301 8 : if (debugLogs) {
302 8 : LogPrintf("CDeterministicMNList::%s -- banned MN %s at height %d\n",
303 16 : __func__, proTxHash.ToString(), nHeight);
304 : }
305 : }
306 106 : UpdateMN(proTxHash, newState);
307 53 : }
308 :
309 2337 : void CDeterministicMNList::PoSeDecrease(const uint256& proTxHash)
310 : {
311 2337 : auto dmn = GetMN(proTxHash);
312 2337 : if (!dmn) {
313 0 : throw(std::runtime_error(strprintf("%s: Can't find a masternode with proTxHash=%s", __func__, proTxHash.ToString())));
314 : }
315 2337 : assert(dmn->pdmnState->nPoSePenalty > 0 && dmn->pdmnState->nPoSeBanHeight == -1);
316 :
317 4674 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
318 2337 : newState->nPoSePenalty--;
319 4674 : UpdateMN(proTxHash, newState);
320 2337 : }
321 :
322 8661 : CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNList& to) const
323 : {
324 8661 : CDeterministicMNListDiff diffRet;
325 :
326 8661 : to.ForEachMN(false, [&](const CDeterministicMNCPtr& toPtr) {
327 38622 : auto fromPtr = GetMN(toPtr->proTxHash);
328 38622 : if (fromPtr == nullptr) {
329 403 : diffRet.addedMNs.emplace_back(toPtr);
330 38219 : } else if (fromPtr != toPtr || fromPtr->pdmnState != toPtr->pdmnState) {
331 18514 : CDeterministicMNStateDiff stateDiff(*fromPtr->pdmnState, *toPtr->pdmnState);
332 9257 : if (stateDiff.fields) {
333 9197 : diffRet.updatedMNs.emplace(toPtr->GetInternalId(), std::move(stateDiff));
334 : }
335 : }
336 38622 : });
337 8661 : ForEachMN(false, [&](const CDeterministicMNCPtr& fromPtr) {
338 38249 : auto toPtr = to.GetMN(fromPtr->proTxHash);
339 38249 : if (toPtr == nullptr) {
340 30 : diffRet.removedMns.emplace(fromPtr->GetInternalId());
341 : }
342 38249 : });
343 :
344 : // added MNs need to be sorted by internalId so that these are added in correct order when the diff is applied later
345 : // otherwise internalIds will not match with the original list
346 8661 : std::sort(diffRet.addedMNs.begin(), diffRet.addedMNs.end(), [](const CDeterministicMNCPtr& a, const CDeterministicMNCPtr& b) {
347 39 : return a->GetInternalId() < b->GetInternalId();
348 : });
349 :
350 8661 : return diffRet;
351 : }
352 :
353 8750 : CDeterministicMNList CDeterministicMNList::ApplyDiff(const CBlockIndex* pindex, const CDeterministicMNListDiff& diff) const
354 : {
355 8750 : CDeterministicMNList result = *this;
356 8750 : result.blockHash = pindex->GetBlockHash();
357 8750 : result.nHeight = pindex->nHeight;
358 :
359 8772 : for (const auto& id : diff.removedMns) {
360 44 : auto dmn = result.GetMNByInternalId(id);
361 22 : if (!dmn) {
362 0 : throw(std::runtime_error(strprintf("%s: can't find a removed masternode, id=%d", __func__, id)));
363 : }
364 22 : result.RemoveMN(dmn->proTxHash);
365 : }
366 9352 : for (const auto& dmn : diff.addedMNs) {
367 602 : result.AddMN(dmn);
368 : }
369 19512 : for (const auto& p : diff.updatedMNs) {
370 21524 : auto dmn = result.GetMNByInternalId(p.first);
371 10762 : result.UpdateMN(dmn, p.second);
372 : }
373 :
374 8750 : return result;
375 : }
376 :
377 1082 : void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount)
378 : {
379 1082 : assert(dmn != nullptr);
380 :
381 1082 : if (mnMap.find(dmn->proTxHash)) {
382 0 : throw(std::runtime_error(strprintf("%s: can't add a duplicate masternode with the same proTxHash=%s", __func__, dmn->proTxHash.ToString())));
383 : }
384 1082 : if (mnInternalIdMap.find(dmn->GetInternalId())) {
385 0 : throw(std::runtime_error(strprintf("%s: can't add a duplicate masternode with the same internalId=%d", __func__, dmn->GetInternalId())));
386 : }
387 1082 : if (HasUniqueProperty(dmn->pdmnState->addr)) {
388 0 : throw(std::runtime_error(strprintf("%s: can't add a masternode with a duplicate address %s", __func__, dmn->pdmnState->addr.ToStringIPPort())));
389 : }
390 1082 : if (HasUniqueProperty(dmn->pdmnState->keyIDOwner) || HasUniqueProperty(dmn->pdmnState->pubKeyOperator)) {
391 0 : throw(std::runtime_error(strprintf("%s: can't add a masternode with a duplicate key (%s or %s)", __func__, EncodeDestination(dmn->pdmnState->keyIDOwner), bls::EncodePublic(Params(), dmn->pdmnState->pubKeyOperator.Get()))));
392 : }
393 :
394 1082 : mnMap = mnMap.set(dmn->proTxHash, dmn);
395 1082 : mnInternalIdMap = mnInternalIdMap.set(dmn->GetInternalId(), dmn->proTxHash);
396 1082 : AddUniqueProperty(dmn, dmn->collateralOutpoint);
397 1082 : if (dmn->pdmnState->addr != CService()) {
398 1082 : AddUniqueProperty(dmn, dmn->pdmnState->addr);
399 : }
400 1082 : AddUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
401 1082 : AddUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator);
402 :
403 1082 : if (fBumpTotalCount) {
404 : // nTotalRegisteredCount acts more like a checkpoint, not as a limit,
405 1082 : nTotalRegisteredCount = std::max(dmn->GetInternalId() + 1, (uint64_t)nTotalRegisteredCount);
406 : }
407 1082 : }
408 :
409 21974 : void CDeterministicMNList::UpdateMN(const CDeterministicMNCPtr& oldDmn, const CDeterministicMNStateCPtr& pdmnState)
410 : {
411 21974 : assert(oldDmn != nullptr);
412 :
413 43868 : if (HasUniqueProperty(oldDmn->pdmnState->addr) && GetUniquePropertyMN(oldDmn->pdmnState->addr)->proTxHash != oldDmn->proTxHash) {
414 0 : throw(std::runtime_error(strprintf("%s: can't update a masternode with a duplicate address %s", __func__, oldDmn->pdmnState->addr.ToStringIPPort())));
415 : }
416 :
417 21974 : auto dmn = std::make_shared<CDeterministicMN>(*oldDmn);
418 43948 : auto oldState = dmn->pdmnState;
419 21974 : dmn->pdmnState = pdmnState;
420 21974 : mnMap = mnMap.set(oldDmn->proTxHash, dmn);
421 :
422 21974 : UpdateUniqueProperty(dmn, oldState->addr, pdmnState->addr);
423 21974 : UpdateUniqueProperty(dmn, oldState->keyIDOwner, pdmnState->keyIDOwner);
424 43948 : UpdateUniqueProperty(dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator);
425 21974 : }
426 :
427 11212 : void CDeterministicMNList::UpdateMN(const uint256& proTxHash, const CDeterministicMNStateCPtr& pdmnState)
428 : {
429 11212 : auto oldDmn = mnMap.find(proTxHash);
430 11212 : if (!oldDmn) {
431 0 : throw(std::runtime_error(strprintf("%s: Can't find a masternode with proTxHash=%s", __func__, proTxHash.ToString())));
432 : }
433 11212 : UpdateMN(*oldDmn, pdmnState);
434 11212 : }
435 :
436 10762 : void CDeterministicMNList::UpdateMN(const CDeterministicMNCPtr& oldDmn, const CDeterministicMNStateDiff& stateDiff)
437 : {
438 10762 : assert(oldDmn != nullptr);
439 10762 : auto oldState = oldDmn->pdmnState;
440 21524 : auto newState = std::make_shared<CDeterministicMNState>(*oldState);
441 10762 : stateDiff.ApplyToState(*newState);
442 21524 : UpdateMN(oldDmn, newState);
443 10762 : }
444 :
445 47 : void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
446 : {
447 47 : auto dmn = GetMN(proTxHash);
448 47 : if (!dmn) {
449 0 : throw(std::runtime_error(strprintf("%s: Can't find a masternode with proTxHash=%s", __func__, proTxHash.ToString())));
450 : }
451 47 : DeleteUniqueProperty(dmn, dmn->collateralOutpoint);
452 47 : if (dmn->pdmnState->addr != CService()) {
453 47 : DeleteUniqueProperty(dmn, dmn->pdmnState->addr);
454 : }
455 47 : DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
456 47 : DeleteUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator);
457 :
458 47 : mnMap = mnMap.erase(proTxHash);
459 47 : mnInternalIdMap = mnInternalIdMap.erase(dmn->GetInternalId());
460 47 : }
461 :
462 772 : CDeterministicMNManager::CDeterministicMNManager(CEvoDB& _evoDb) :
463 772 : evoDb(_evoDb)
464 : {
465 772 : }
466 :
467 56422 : bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& _state, bool fJustCheck)
468 : {
469 56422 : int nHeight = pindex->nHeight;
470 56422 : if (!IsDIP3Enforced(nHeight)) {
471 : // nothing to do
472 : return true;
473 : }
474 :
475 20018 : CDeterministicMNList oldList, newList;
476 10009 : CDeterministicMNListDiff diff;
477 :
478 10009 : try {
479 10009 : LOCK(cs);
480 :
481 10009 : if (!BuildNewListFromBlock(block, pindex->pprev, _state, newList, true)) {
482 : // pass the state returned by the function above
483 1378 : return false;
484 : }
485 :
486 9997 : if (fJustCheck) {
487 : return true;
488 : }
489 :
490 8631 : if (newList.GetHeight() == -1) {
491 0 : newList.SetHeight(nHeight);
492 : }
493 :
494 8631 : newList.SetBlockHash(block.GetHash());
495 :
496 8631 : oldList = GetListForBlock(pindex->pprev);
497 8631 : diff = oldList.BuildDiff(newList);
498 :
499 8631 : evoDb.Write(std::make_pair(DB_LIST_DIFF, newList.GetBlockHash()), diff);
500 8631 : if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0 || oldList.GetHeight() == -1) {
501 72 : evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, newList.GetBlockHash()), newList);
502 72 : mnListsCache.emplace(newList.GetBlockHash(), newList);
503 72 : LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.allMNsCount=%d\n",
504 72 : __func__, nHeight, newList.GetAllMNsCount());
505 : }
506 :
507 8631 : diff.nHeight = pindex->nHeight;
508 8631 : mnListDiffsCache.emplace(pindex->GetBlockHash(), diff);
509 0 : } catch (const std::exception& e) {
510 0 : LogPrintf("CDeterministicMNManager::%s -- internal error: %s\n", __func__, e.what());
511 0 : return _state.DoS(100, false, REJECT_INVALID, "failed-dmn-block");
512 : }
513 :
514 : // Don't hold cs while calling signals
515 8631 : if (diff.HasChanges()) {
516 7221 : GetMainSignals().NotifyMasternodeListChanged(false, oldList, diff);
517 7221 : uiInterface.NotifyMasternodeListChanged(newList);
518 : }
519 :
520 18640 : LOCK(cs);
521 8631 : CleanupCache(nHeight);
522 :
523 8631 : return true;
524 : }
525 :
526 1457 : bool CDeterministicMNManager::UndoBlock(const CBlock& block, const CBlockIndex* pindex)
527 : {
528 1457 : if (!IsDIP3Enforced(pindex->nHeight)) {
529 : // nothing to do
530 : return true;
531 : }
532 :
533 30 : const uint256& blockHash = block.GetHash();
534 :
535 60 : CDeterministicMNList curList;
536 60 : CDeterministicMNList prevList;
537 30 : CDeterministicMNListDiff diff;
538 30 : {
539 30 : LOCK(cs);
540 30 : evoDb.Read(std::make_pair(DB_LIST_DIFF, blockHash), diff);
541 :
542 30 : if (diff.HasChanges()) {
543 : // need to call this before erasing
544 30 : curList = GetListForBlock(pindex);
545 30 : prevList = GetListForBlock(pindex->pprev);
546 : }
547 :
548 30 : mnListsCache.erase(blockHash);
549 30 : mnListDiffsCache.erase(blockHash);
550 : }
551 :
552 30 : if (diff.HasChanges()) {
553 60 : auto inversedDiff = curList.BuildDiff(prevList);
554 30 : GetMainSignals().NotifyMasternodeListChanged(true, curList, inversedDiff);
555 30 : uiInterface.NotifyMasternodeListChanged(prevList);
556 : }
557 :
558 30 : return true;
559 : }
560 :
561 42252 : void CDeterministicMNManager::SetTipIndex(const CBlockIndex* pindex)
562 : {
563 42252 : LOCK(cs);
564 42252 : tipIndex = pindex;
565 42252 : }
566 :
567 10009 : bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state, CDeterministicMNList& mnListRet, bool debugLogs)
568 : {
569 10009 : AssertLockHeld(cs);
570 10009 : const auto& consensus = Params().GetConsensus();
571 10009 : int nHeight = pindexPrev->nHeight + 1;
572 :
573 20018 : CDeterministicMNList oldList = GetListForBlock(pindexPrev);
574 10009 : CDeterministicMNList newList = oldList;
575 10009 : newList.SetBlockHash(UINT256_ZERO); // we can't know the final block hash, so better not return a (invalid) block hash
576 10009 : newList.SetHeight(nHeight);
577 :
578 20018 : auto payee = oldList.GetMNPayee();
579 :
580 : // we iterate the oldList here and update the newList
581 : // this is only valid as long these have not diverged at this point, which is the case as long as we don't add
582 : // code above this loop that modifies newList
583 10009 : oldList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) {
584 89564 : if (!dmn->pdmnState->confirmedHash.IsNull()) {
585 : // already confirmed
586 : return;
587 : }
588 : // this works on the previous block, so confirmation will happen one block after nMasternodeMinimumConfirmations
589 : // has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations
590 1431 : int nConfirmations = pindexPrev->nHeight - dmn->pdmnState->nRegisteredHeight;
591 956 : if (nConfirmations >= consensus.MasternodeCollateralMinConf()) {
592 950 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
593 475 : newState->UpdateConfirmedHash(dmn->proTxHash, pindexPrev->GetBlockHash());
594 950 : newList.UpdateMN(dmn->proTxHash, newState);
595 : }
596 : });
597 :
598 10009 : DecreasePoSePenalties(newList);
599 :
600 : // we skip the coinbase
601 18422 : for (int i = 1; i < (int)block.vtx.size(); i++) {
602 8425 : const CTransaction& tx = *block.vtx[i];
603 :
604 8425 : if (tx.nType == CTransaction::TxType::PROREG) {
605 486 : ProRegPL pl;
606 486 : if (!GetTxPayload(tx, pl)) {
607 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
608 : }
609 :
610 966 : auto dmn = std::make_shared<CDeterministicMN>(newList.GetTotalRegisteredCount());
611 486 : dmn->proTxHash = tx.GetHash();
612 :
613 : // collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself
614 972 : dmn->collateralOutpoint = pl.collateralOutpoint.hash.IsNull() ? COutPoint(tx.GetHash(), pl.collateralOutpoint.n)
615 : : pl.collateralOutpoint;
616 :
617 : // if the collateral outpoint appears in the legacy masternode list, remove the old node
618 : // !TODO: remove this when the transition to DMN is complete
619 486 : CMasternode* old_mn = mnodeman.Find(dmn->collateralOutpoint);
620 486 : if (old_mn) {
621 7 : old_mn->SetSpent();
622 7 : mnodeman.CheckAndRemove();
623 : }
624 :
625 966 : auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint);
626 486 : if (replacedDmn != nullptr) {
627 : // This might only happen with a ProRegTx that refers an external collateral
628 : // In that case the new ProRegTx will replace the old one. This means the old one is removed
629 : // and the new one is added like a completely fresh one, which is also at the bottom of the payment list
630 9 : newList.RemoveMN(replacedDmn->proTxHash);
631 9 : if (debugLogs) {
632 18 : LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was used for a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
633 27 : __func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount());
634 : }
635 : }
636 :
637 486 : if (newList.HasUniqueProperty(pl.addr)) {
638 6 : return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-IP-address");
639 : }
640 484 : if (newList.HasUniqueProperty(pl.keyIDOwner)) {
641 6 : return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-owner-key");
642 : }
643 482 : if (newList.HasUniqueProperty(pl.pubKeyOperator)) {
644 6 : return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-operator-key");
645 : }
646 :
647 480 : dmn->nOperatorReward = pl.nOperatorReward;
648 :
649 960 : auto dmnState = std::make_shared<CDeterministicMNState>(pl);
650 480 : dmnState->nRegisteredHeight = nHeight;
651 480 : if (pl.addr == CService()) {
652 : // start in banned pdmnState as we need to wait for a ProUpServTx
653 0 : dmnState->nPoSeBanHeight = nHeight;
654 : }
655 480 : dmn->pdmnState = dmnState;
656 :
657 480 : newList.AddMN(dmn);
658 :
659 480 : if (debugLogs) {
660 480 : LogPrintf("CDeterministicMNManager::%s -- MN %s added at height %d: %s\n",
661 1440 : __func__, tx.GetHash().ToString(), nHeight, pl.ToString());
662 : }
663 :
664 7939 : } else if (tx.nType == CTransaction::TxType::PROUPSERV) {
665 35 : ProUpServPL pl;
666 35 : if (!GetTxPayload(tx, pl)) {
667 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
668 : }
669 :
670 47 : if (newList.HasUniqueProperty(pl.addr) && newList.GetUniquePropertyMN(pl.addr)->proTxHash != pl.proTxHash) {
671 6 : return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
672 : }
673 :
674 66 : CDeterministicMNCPtr dmn = newList.GetMN(pl.proTxHash);
675 33 : if (!dmn) {
676 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
677 : }
678 56 : if (dmn->nOperatorReward == 0 && !pl.scriptOperatorPayout.empty()) {
679 : // operator payout address can not be set if the operator reward is 0
680 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-operator-payee");
681 : }
682 66 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
683 33 : newState->addr = pl.addr;
684 33 : newState->scriptOperatorPayout = pl.scriptOperatorPayout;
685 :
686 33 : if (newState->nPoSeBanHeight != -1) {
687 : // only revive when all keys are set
688 30 : if (newState->pubKeyOperator.Get().IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) {
689 10 : newState->nPoSePenalty = 0;
690 10 : newState->nPoSeBanHeight = -1;
691 10 : newState->nPoSeRevivedHeight = nHeight;
692 :
693 10 : if (debugLogs) {
694 10 : LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n",
695 20 : __func__, pl.proTxHash.ToString(), nHeight);
696 : }
697 : }
698 : }
699 :
700 33 : newList.UpdateMN(pl.proTxHash, newState);
701 33 : if (debugLogs) {
702 33 : LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
703 99 : __func__, pl.proTxHash.ToString(), nHeight, pl.ToString());
704 : }
705 :
706 7904 : } else if (tx.nType == CTransaction::TxType::PROUPREG) {
707 55 : ProUpRegPL pl;
708 55 : if (!GetTxPayload(tx, pl)) {
709 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
710 : }
711 :
712 106 : CDeterministicMNCPtr dmn = newList.GetMN(pl.proTxHash);
713 55 : if (!dmn) {
714 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
715 : }
716 80 : if (newList.HasUniqueProperty(pl.pubKeyOperator) && newList.GetUniquePropertyMN(pl.pubKeyOperator)->proTxHash != pl.proTxHash) {
717 12 : return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-operator-key");
718 : }
719 102 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
720 51 : if (newState->pubKeyOperator.Get() != pl.pubKeyOperator) {
721 : // reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
722 30 : newState->ResetOperatorFields();
723 30 : newState->BanIfNotBanned(nHeight);
724 : }
725 51 : newState->pubKeyOperator.Set(pl.pubKeyOperator);
726 51 : newState->keyIDVoting = pl.keyIDVoting;
727 51 : newState->scriptPayout = pl.scriptPayout;
728 :
729 51 : newList.UpdateMN(pl.proTxHash, newState);
730 :
731 51 : if (debugLogs) {
732 51 : LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
733 153 : __func__, pl.proTxHash.ToString(), nHeight, pl.ToString());
734 : }
735 :
736 7849 : } else if (tx.nType == CTransaction::TxType::PROUPREV) {
737 22 : ProUpRevPL pl;
738 22 : if (!GetTxPayload(tx, pl)) {
739 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
740 : }
741 :
742 44 : CDeterministicMNCPtr dmn = newList.GetMN(pl.proTxHash);
743 22 : if (!dmn) {
744 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
745 : }
746 44 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
747 22 : newState->ResetOperatorFields();
748 22 : newState->BanIfNotBanned(nHeight);
749 22 : newState->nRevocationReason = pl.nReason;
750 :
751 22 : newList.UpdateMN(pl.proTxHash, newState);
752 :
753 22 : if (debugLogs) {
754 22 : LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
755 66 : __func__, pl.proTxHash.ToString(), nHeight, pl.ToString());
756 : }
757 7827 : } else if (tx.nType == CTransaction::TxType::LLMQCOMM) {
758 5058 : llmq::LLMQCommPL pl;
759 2529 : if (!GetTxPayload(tx, pl)) {
760 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
761 : }
762 2529 : if (!pl.commitment.IsNull()) {
763 : // Double-check that the quorum index is in the active chain
764 170 : const auto& params = consensus.llmqs.at((Consensus::LLMQType)pl.commitment.llmqType);
765 170 : uint32_t quorumHeight = pl.nHeight - (pl.nHeight % params.dkgInterval);
766 170 : auto quorumIndex = pindexPrev->GetAncestor(quorumHeight);
767 170 : if (!quorumIndex || quorumIndex->GetBlockHash() != pl.commitment.quorumHash) {
768 0 : return _state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash");
769 : }
770 : // Check for failed DKG participation by MNs
771 170 : HandleQuorumCommitment(pl.commitment, quorumIndex, newList, debugLogs);
772 : }
773 : }
774 :
775 : }
776 :
777 : // check if any existing MN collateral is spent by this transaction
778 : // we skip the coinbase
779 18392 : for (int i = 1; i < (int)block.vtx.size(); i++) {
780 8395 : const CTransaction& tx = *block.vtx[i];
781 14336 : for (const auto& in : tx.vin) {
782 11882 : auto dmn = newList.GetMNByCollateral(in.prevout);
783 5957 : if (dmn && dmn->collateralOutpoint == in.prevout) {
784 16 : newList.RemoveMN(dmn->proTxHash);
785 16 : if (debugLogs) {
786 32 : LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
787 48 : __func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount());
788 : }
789 : }
790 : }
791 : }
792 :
793 : // The payee for the current block was determined by the previous block's list but it might have disappeared in the
794 : // current block. We still pay that MN one last time however.
795 9997 : if (payee && newList.HasMN(payee->proTxHash)) {
796 16482 : auto newState = std::make_shared<CDeterministicMNState>(*newList.GetMN(payee->proTxHash)->pdmnState);
797 8241 : newState->nLastPaidHeight = nHeight;
798 16482 : newList.UpdateMN(payee->proTxHash, newState);
799 : }
800 :
801 9997 : mnListRet = std::move(newList);
802 :
803 9997 : return true;
804 : }
805 :
806 170 : void CDeterministicMNManager::HandleQuorumCommitment(llmq::CFinalCommitment& qc, const CBlockIndex* pindexQuorum, CDeterministicMNList& mnList, bool debugLogs)
807 : {
808 : // The commitment has already been validated at this point so it's safe to use members of it
809 :
810 340 : auto members = GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, pindexQuorum);
811 :
812 680 : for (size_t i = 0; i < members.size(); i++) {
813 510 : if (!mnList.HasMN(members[i]->proTxHash)) {
814 0 : continue;
815 : }
816 510 : if (!qc.validMembers[i]) {
817 : // punish MN for failed DKG participation
818 : // The idea is to immediately ban a MN when it fails 2 DKG sessions with only a few blocks in-between
819 : // If there were enough blocks between failures, the MN has a chance to recover as he reduces his penalty by 1 for every block
820 : // If it however fails 3 times in the timespan of a single payment cycle, it should definitely get banned
821 53 : mnList.PoSePunish(members[i]->proTxHash, mnList.CalcPenalty(66), debugLogs);
822 : }
823 : }
824 170 : }
825 :
826 10009 : void CDeterministicMNManager::DecreasePoSePenalties(CDeterministicMNList& mnList)
827 : {
828 10009 : std::vector<uint256> toDecrease;
829 10009 : toDecrease.reserve(mnList.GetValidMNsCount() / 10);
830 : // only iterate and decrease for valid ones (not PoSe banned yet)
831 : // if a MN ever reaches the maximum, it stays in PoSe banned state until revived
832 10009 : mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
833 44339 : if (dmn->pdmnState->nPoSePenalty > 0 && dmn->pdmnState->nPoSeBanHeight == -1) {
834 2337 : toDecrease.emplace_back(dmn->proTxHash);
835 : }
836 44339 : });
837 :
838 12346 : for (const auto& proTxHash : toDecrease) {
839 2337 : mnList.PoSeDecrease(proTxHash);
840 : }
841 10009 : }
842 :
843 395330 : CDeterministicMNList CDeterministicMNManager::GetListForBlock(const CBlockIndex* pindex)
844 : {
845 790660 : LOCK(cs);
846 :
847 : // Return early before enforcement
848 395330 : if (!IsDIP3Enforced(pindex->nHeight)) {
849 304674 : return {};
850 : }
851 :
852 90656 : CDeterministicMNList snapshot;
853 181312 : std::list<const CBlockIndex*> listDiffIndexes;
854 :
855 100781 : while (true) {
856 : // try using cache before reading from disk
857 100781 : auto itLists = mnListsCache.find(pindex->GetBlockHash());
858 100781 : if (itLists != mnListsCache.end()) {
859 90654 : snapshot = itLists->second;
860 90656 : break;
861 : }
862 :
863 10127 : if (evoDb.Read(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), snapshot)) {
864 2 : mnListsCache.emplace(pindex->GetBlockHash(), snapshot);
865 2 : break;
866 : }
867 :
868 : // no snapshot found yet, check diffs
869 10125 : auto itDiffs = mnListDiffsCache.find(pindex->GetBlockHash());
870 10125 : if (itDiffs != mnListDiffsCache.end()) {
871 10016 : listDiffIndexes.emplace_front(pindex);
872 10016 : pindex = pindex->pprev;
873 10016 : continue;
874 : }
875 :
876 109 : CDeterministicMNListDiff diff;
877 109 : if (!evoDb.Read(std::make_pair(DB_LIST_DIFF, pindex->GetBlockHash()), diff)) {
878 : // no snapshot and no diff on disk means that it's initial snapshot (empty list)
879 : // If we get here, then this must be the block before the enforcement of DIP3.
880 0 : if (!IsActivationHeight(pindex->nHeight + 1, Params().GetConsensus(), Consensus::UPGRADE_V6_0)) {
881 0 : std::string err = strprintf("No masternode list data found for block %s at height %d. "
882 0 : "Possible corrupt database.", pindex->GetBlockHash().ToString(), pindex->nHeight);
883 0 : throw std::runtime_error(err);
884 : }
885 0 : snapshot = CDeterministicMNList(pindex->GetBlockHash(), -1, 0);
886 0 : mnListsCache.emplace(pindex->GetBlockHash(), snapshot);
887 0 : break;
888 : }
889 :
890 109 : diff.nHeight = pindex->nHeight;
891 109 : mnListDiffsCache.emplace(pindex->GetBlockHash(), std::move(diff));
892 109 : listDiffIndexes.emplace_front(pindex);
893 109 : pindex = pindex->pprev;
894 : }
895 :
896 100781 : for (const auto& diffIndex : listDiffIndexes) {
897 10125 : const auto& diff = mnListDiffsCache.at(diffIndex->GetBlockHash());
898 10125 : if (diff.HasChanges()) {
899 8750 : snapshot = snapshot.ApplyDiff(diffIndex, diff);
900 : } else {
901 1375 : snapshot.SetBlockHash(diffIndex->GetBlockHash());
902 10125 : snapshot.SetHeight(diffIndex->nHeight);
903 : }
904 : }
905 :
906 90656 : if (tipIndex) {
907 : // always keep a snapshot for the tip
908 90628 : if (snapshot.GetBlockHash() == tipIndex->GetBlockHash()) {
909 81352 : mnListsCache.emplace(snapshot.GetBlockHash(), snapshot);
910 : } else {
911 : // !TODO: keep snapshots for yet alive quorums
912 : }
913 : }
914 :
915 90656 : return snapshot;
916 : }
917 :
918 353905 : CDeterministicMNList CDeterministicMNManager::GetListAtChainTip()
919 : {
920 707810 : LOCK(cs);
921 353905 : if (!tipIndex) {
922 319 : return {};
923 : }
924 353586 : return GetListForBlock(tipIndex);
925 : }
926 :
927 532611 : bool CDeterministicMNManager::IsDIP3Enforced(int nHeight) const
928 : {
929 532611 : return Params().GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0);
930 : }
931 :
932 33604 : bool CDeterministicMNManager::IsDIP3Enforced() const
933 : {
934 67208 : int tipHeight = WITH_LOCK(cs, return tipIndex ? tipIndex->nHeight : -1;);
935 33604 : return IsDIP3Enforced(tipHeight);
936 : }
937 :
938 229324 : bool CDeterministicMNManager::LegacyMNObsolete(int nHeight) const
939 : {
940 229324 : return nHeight > sporkManager.GetSporkValue(SPORK_21_LEGACY_MNS_MAX_HEIGHT);
941 : }
942 :
943 167103 : bool CDeterministicMNManager::LegacyMNObsolete() const
944 : {
945 334206 : int tipHeight = WITH_LOCK(cs, return tipIndex ? tipIndex->nHeight : -1;);
946 167103 : return LegacyMNObsolete(tipHeight);
947 : }
948 :
949 8631 : void CDeterministicMNManager::CleanupCache(int nHeight)
950 : {
951 8631 : AssertLockHeld(cs);
952 :
953 8631 : std::vector<uint256> toDeleteLists;
954 8631 : std::vector<uint256> toDeleteDiffs;
955 893874 : for (const auto& p : mnListsCache) {
956 885243 : if (p.second.GetHeight() + LIST_DIFFS_CACHE_SIZE < nHeight) {
957 0 : toDeleteLists.emplace_back(p.first);
958 0 : continue;
959 : }
960 : // !TODO: llmq cache cleanup
961 : }
962 8631 : for (const auto& h : toDeleteLists) {
963 0 : mnListsCache.erase(h);
964 : }
965 903675 : for (const auto& p : mnListDiffsCache) {
966 895044 : if (p.second.nHeight + LIST_DIFFS_CACHE_SIZE < nHeight) {
967 0 : toDeleteDiffs.emplace_back(p.first);
968 : }
969 : }
970 8631 : for (const auto& h : toDeleteDiffs) {
971 0 : mnListDiffsCache.erase(h);
972 : }
973 8631 : }
974 :
975 4869 : std::vector<CDeterministicMNCPtr> CDeterministicMNManager::GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
976 : {
977 4869 : auto& params = Params().GetConsensus().llmqs.at(llmqType);
978 9738 : auto allMns = GetListForBlock(pindexQuorum);
979 4869 : auto modifier = ::SerializeHash(std::make_pair(static_cast<uint8_t>(llmqType), pindexQuorum->GetBlockHash()));
980 9738 : return allMns.CalculateQuorum(params.size, modifier);
981 : }
982 :
983 :
|