LCOV - code coverage report
Current view: top level - src/evo - deterministicmns.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 494 553 89.3 %
Date: 2025-04-02 01:23:23 Functions: 51 54 94.4 %

          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        1260 : void CDeterministicMNState::ToJson(UniValue& obj) const
      47             : {
      48        1260 :     obj.clear();
      49        1260 :     obj.setObject();
      50        2520 :     obj.pushKV("service", addr.ToStringIPPort());
      51        1260 :     obj.pushKV("registeredHeight", nRegisteredHeight);
      52        1260 :     obj.pushKV("lastPaidHeight", nLastPaidHeight);
      53        1260 :     obj.pushKV("PoSePenalty", nPoSePenalty);
      54        1260 :     obj.pushKV("PoSeRevivedHeight", nPoSeRevivedHeight);
      55        1260 :     obj.pushKV("PoSeBanHeight", nPoSeBanHeight);
      56        1260 :     obj.pushKV("revocationReason", nRevocationReason);
      57        3780 :     obj.pushKV("ownerAddress", EncodeDestination(keyIDOwner));
      58        2520 :     obj.pushKV("operatorPubKey", bls::EncodePublic(Params(), pubKeyOperator.Get()));
      59        3780 :     obj.pushKV("votingAddress", EncodeDestination(keyIDVoting));
      60             : 
      61        1260 :     CTxDestination dest1;
      62        1260 :     if (ExtractDestination(scriptPayout, dest1)) {
      63        3780 :         obj.pushKV("payoutAddress", EncodeDestination(dest1));
      64             :     }
      65        2520 :     CTxDestination dest2;
      66        1260 :     if (ExtractDestination(scriptOperatorPayout, dest2)) {
      67         246 :         obj.pushKV("operatorPayoutAddress", EncodeDestination(dest2));
      68             :     }
      69        1260 : }
      70             : 
      71       29335 : uint64_t CDeterministicMN::GetInternalId() const
      72             : {
      73             :     // can't get it if it wasn't set yet
      74       29335 :     assert(internalId != std::numeric_limits<uint64_t>::max());
      75       29335 :     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        1260 : void CDeterministicMN::ToJson(UniValue& obj) const
      84             : {
      85        1260 :     obj.clear();
      86        1260 :     obj.setObject();
      87             : 
      88        1260 :     UniValue stateObj;
      89        1260 :     pdmnState->ToJson(stateObj);
      90             : 
      91        2520 :     obj.pushKV("proTxHash", proTxHash.ToString());
      92        2520 :     obj.pushKV("collateralHash", collateralOutpoint.hash.ToString());
      93        1260 :     obj.pushKV("collateralIndex", (int)collateralOutpoint.n);
      94        1260 :     obj.pushKV("operatorReward", (double)nOperatorReward / 100);
      95        2520 :     obj.pushKV("dmnstate", stateObj);
      96        1260 : }
      97             : 
      98      142461 : CDeterministicMNCPtr CDeterministicMNList::GetMN(const uint256& proTxHash) const
      99             : {
     100      142461 :     auto p = mnMap.find(proTxHash);
     101      142461 :     if (p == nullptr) {
     102         633 :         return nullptr;
     103             :     }
     104      141828 :     return *p;
     105             : }
     106             : 
     107        5050 : CDeterministicMNCPtr CDeterministicMNList::GetValidMN(const uint256& proTxHash) const
     108             : {
     109        5050 :     auto dmn = GetMN(proTxHash);
     110        5050 :     if (dmn && dmn->IsPoSeBanned()) {
     111          37 :         return nullptr;
     112             :     }
     113        5050 :     return dmn;
     114             : }
     115             : 
     116         301 : CDeterministicMNCPtr CDeterministicMNList::GetMNByOperatorKey(const CBLSPublicKey& pubKey)
     117             : {
     118        1211 :     for (const auto& p : mnMap) {
     119        1238 :         if (p.second->pdmnState->pubKeyOperator.Get() == pubKey) {
     120          91 :             return p.second;
     121             :         }
     122             :     }
     123         210 :     return nullptr;
     124             : }
     125             : 
     126      406028 : CDeterministicMNCPtr CDeterministicMNList::GetMNByCollateral(const COutPoint& collateralOutpoint) const
     127             : {
     128      406028 :     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       10284 : CDeterministicMNCPtr CDeterministicMNList::GetMNByInternalId(uint64_t internalId) const
     146             : {
     147       10284 :     auto proTxHash = mnInternalIdMap.find(internalId);
     148       10284 :     if (!proTxHash) {
     149           0 :         return nullptr;
     150             :     }
     151       10284 :     return GetMN(*proTxHash);
     152             : }
     153             : 
     154      147978 : static int CompareByLastPaidGetHeight(const CDeterministicMN& dmn)
     155             : {
     156      147978 :     int height = dmn.pdmnState->nLastPaidHeight;
     157      147978 :     if (dmn.pdmnState->nPoSeRevivedHeight != -1 && dmn.pdmnState->nPoSeRevivedHeight > height) {
     158      147978 :         height = dmn.pdmnState->nPoSeRevivedHeight;
     159      147730 :     } else if (height == 0) {
     160        5935 :         height = dmn.pdmnState->nRegisteredHeight;
     161             :     }
     162      147978 :     return height;
     163             : }
     164             : 
     165       73989 : static bool CompareByLastPaid(const CDeterministicMN& _a, const CDeterministicMN& _b)
     166             : {
     167       73989 :     int ah = CompareByLastPaidGetHeight(_a);
     168       73989 :     int bh = CompareByLastPaidGetHeight(_b);
     169       73989 :     if (ah == bh) {
     170        1227 :         return _a.proTxHash < _b.proTxHash;
     171             :     } else {
     172       72762 :         return ah < bh;
     173             :     }
     174             : }
     175       73989 : static bool CompareByLastPaid(const CDeterministicMNCPtr& _a, const CDeterministicMNCPtr& _b)
     176             : {
     177       73989 :     return CompareByLastPaid(*_a, *_b);
     178             : }
     179             : 
     180       17630 : CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const
     181             : {
     182       17630 :     if (mnMap.size() == 0) {
     183        1861 :         return nullptr;
     184             :     }
     185             : 
     186       33399 :     CDeterministicMNCPtr best;
     187       15769 :     ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
     188       89758 :         if (!best || CompareByLastPaid(dmn, best)) {
     189       34994 :             best = dmn;
     190             :         }
     191       89758 :     });
     192             : 
     193       15769 :     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        4306 : std::vector<CDeterministicMNCPtr> CDeterministicMNList::CalculateQuorum(size_t maxSize, const uint256& modifier) const
     218             : {
     219        4306 :     auto scores = CalculateScores(modifier);
     220             : 
     221             :     // sort is descending order
     222        4306 :     std::sort(scores.rbegin(), scores.rend(), [](const std::pair<arith_uint256, CDeterministicMNCPtr>& a, std::pair<arith_uint256, CDeterministicMNCPtr>& b) {
     223       59348 :         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       59348 :         return a.first < b.first;
     228             :     });
     229             : 
     230             :     // take top maxSize entries and return it
     231        4306 :     std::vector<CDeterministicMNCPtr> result;
     232        4317 :     result.resize(std::min(maxSize, scores.size()));
     233       17195 :     for (size_t i = 0; i < result.size(); i++) {
     234       12889 :         result[i] = std::move(scores[i].second);
     235             :     }
     236        4306 :     return result;
     237             : }
     238             : 
     239        4306 : std::vector<std::pair<arith_uint256, CDeterministicMNCPtr>> CDeterministicMNList::CalculateScores(const uint256& modifier) const
     240             : {
     241        4306 :     std::vector<std::pair<arith_uint256, CDeterministicMNCPtr>> scores;
     242        4306 :     scores.reserve(GetAllMNsCount());
     243        4306 :     ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
     244       50952 :         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          21 :             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       25455 :         uint256 h;
     254       25455 :         CSHA256 sha256;
     255       25455 :         sha256.Write(dmn->pdmnState->confirmedHashWithProRegTxHash.begin(), dmn->pdmnState->confirmedHashWithProRegTxHash.size());
     256       25455 :         sha256.Write(modifier.begin(), modifier.size());
     257       25455 :         sha256.Finalize(h.begin());
     258             : 
     259       25455 :         scores.emplace_back(UintToArith256(h), dmn);
     260             :     });
     261             : 
     262        4306 :     return scores;
     263             : }
     264             : 
     265          90 : 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          90 :     return std::max(100, (int)GetAllMNsCount());
     271             : }
     272             : 
     273          45 : int CDeterministicMNList::CalcPenalty(int percent) const
     274             : {
     275          45 :     assert(percent > 0);
     276          45 :     return (CalcMaxPoSePenalty() * percent) / 100;
     277             : }
     278             : 
     279          45 : void CDeterministicMNList::PoSePunish(const uint256& proTxHash, int penalty, bool debugLogs)
     280             : {
     281          45 :     assert(penalty > 0);
     282             : 
     283          45 :     auto dmn = GetMN(proTxHash);
     284          45 :     if (!dmn) {
     285           0 :         throw(std::runtime_error(strprintf("%s: Can't find a masternode with proTxHash=%s", __func__, proTxHash.ToString())));
     286             :     }
     287             : 
     288          45 :     int maxPenalty = CalcMaxPoSePenalty();
     289             : 
     290          90 :     auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
     291          45 :     newState->nPoSePenalty += penalty;
     292          82 :     newState->nPoSePenalty = std::min(maxPenalty, newState->nPoSePenalty);
     293             : 
     294          45 :     if (debugLogs) {
     295          45 :         LogPrintf("CDeterministicMNList::%s -- punished MN %s, penalty %d->%d (max=%d)\n",
     296          90 :                   __func__, proTxHash.ToString(), dmn->pdmnState->nPoSePenalty, newState->nPoSePenalty, maxPenalty);
     297             :     }
     298             : 
     299          45 :     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          90 :     UpdateMN(proTxHash, newState);
     307          45 : }
     308             : 
     309        2017 : void CDeterministicMNList::PoSeDecrease(const uint256& proTxHash)
     310             : {
     311        2017 :     auto dmn = GetMN(proTxHash);
     312        2017 :     if (!dmn) {
     313           0 :         throw(std::runtime_error(strprintf("%s: Can't find a masternode with proTxHash=%s", __func__, proTxHash.ToString())));
     314             :     }
     315        2017 :     assert(dmn->pdmnState->nPoSePenalty > 0 && dmn->pdmnState->nPoSeBanHeight == -1);
     316             : 
     317        4034 :     auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
     318        2017 :     newState->nPoSePenalty--;
     319        4034 :     UpdateMN(proTxHash, newState);
     320        2017 : }
     321             : 
     322        8381 : CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNList& to) const
     323             : {
     324        8381 :     CDeterministicMNListDiff diffRet;
     325             : 
     326        8381 :     to.ForEachMN(false, [&](const CDeterministicMNCPtr& toPtr) {
     327       36942 :         auto fromPtr = GetMN(toPtr->proTxHash);
     328       36942 :         if (fromPtr == nullptr) {
     329         403 :             diffRet.addedMNs.emplace_back(toPtr);
     330       36539 :         } else if (fromPtr != toPtr || fromPtr->pdmnState != toPtr->pdmnState) {
     331       17566 :             CDeterministicMNStateDiff stateDiff(*fromPtr->pdmnState, *toPtr->pdmnState);
     332        8783 :             if (stateDiff.fields) {
     333        8723 :                 diffRet.updatedMNs.emplace(toPtr->GetInternalId(), std::move(stateDiff));
     334             :             }
     335             :         }
     336       36942 :     });
     337        8381 :     ForEachMN(false, [&](const CDeterministicMNCPtr& fromPtr) {
     338       36569 :         auto toPtr = to.GetMN(fromPtr->proTxHash);
     339       36569 :         if (toPtr == nullptr) {
     340          30 :             diffRet.removedMns.emplace(fromPtr->GetInternalId());
     341             :         }
     342       36569 :     });
     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        8381 :     std::sort(diffRet.addedMNs.begin(), diffRet.addedMNs.end(), [](const CDeterministicMNCPtr& a, const CDeterministicMNCPtr& b) {
     347          43 :         return a->GetInternalId() < b->GetInternalId();
     348             :     });
     349             : 
     350        8381 :     return diffRet;
     351             : }
     352             : 
     353        8443 : CDeterministicMNList CDeterministicMNList::ApplyDiff(const CBlockIndex* pindex, const CDeterministicMNListDiff& diff) const
     354             : {
     355        8443 :     CDeterministicMNList result = *this;
     356        8443 :     result.blockHash = pindex->GetBlockHash();
     357        8443 :     result.nHeight = pindex->nHeight;
     358             : 
     359        8465 :     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        9033 :     for (const auto& dmn : diff.addedMNs) {
     367         590 :         result.AddMN(dmn);
     368             :     }
     369       18705 :     for (const auto& p : diff.updatedMNs) {
     370       20524 :         auto dmn = result.GetMNByInternalId(p.first);
     371       10262 :         result.UpdateMN(dmn, p.second);
     372             :     }
     373             : 
     374        8443 :     return result;
     375             : }
     376             : 
     377        1070 : void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount)
     378             : {
     379        1070 :     assert(dmn != nullptr);
     380             : 
     381        1070 :     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        1070 :     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        1070 :     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        1070 :     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        1070 :     mnMap = mnMap.set(dmn->proTxHash, dmn);
     395        1070 :     mnInternalIdMap = mnInternalIdMap.set(dmn->GetInternalId(), dmn->proTxHash);
     396        1070 :     AddUniqueProperty(dmn, dmn->collateralOutpoint);
     397        1070 :     if (dmn->pdmnState->addr != CService()) {
     398        1070 :         AddUniqueProperty(dmn, dmn->pdmnState->addr);
     399             :     }
     400        1070 :     AddUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
     401        1070 :     AddUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator);
     402             : 
     403        1070 :     if (fBumpTotalCount) {
     404             :         // nTotalRegisteredCount acts more like a checkpoint, not as a limit,
     405        1070 :         nTotalRegisteredCount = std::max(dmn->GetInternalId() + 1, (uint64_t)nTotalRegisteredCount);
     406             :     }
     407        1070 : }
     408             : 
     409       20835 : void CDeterministicMNList::UpdateMN(const CDeterministicMNCPtr& oldDmn, const CDeterministicMNStateCPtr& pdmnState)
     410             : {
     411       20835 :     assert(oldDmn != nullptr);
     412             : 
     413       41607 :     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       20835 :     auto dmn = std::make_shared<CDeterministicMN>(*oldDmn);
     418       41670 :     auto oldState = dmn->pdmnState;
     419       20835 :     dmn->pdmnState = pdmnState;
     420       20835 :     mnMap = mnMap.set(oldDmn->proTxHash, dmn);
     421             : 
     422       20835 :     UpdateUniqueProperty(dmn, oldState->addr, pdmnState->addr);
     423       20835 :     UpdateUniqueProperty(dmn, oldState->keyIDOwner, pdmnState->keyIDOwner);
     424       41670 :     UpdateUniqueProperty(dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator);
     425       20835 : }
     426             : 
     427       10573 : void CDeterministicMNList::UpdateMN(const uint256& proTxHash, const CDeterministicMNStateCPtr& pdmnState)
     428             : {
     429       10573 :     auto oldDmn = mnMap.find(proTxHash);
     430       10573 :     if (!oldDmn) {
     431           0 :         throw(std::runtime_error(strprintf("%s: Can't find a masternode with proTxHash=%s", __func__, proTxHash.ToString())));
     432             :     }
     433       10573 :     UpdateMN(*oldDmn, pdmnState);
     434       10573 : }
     435             : 
     436       10262 : void CDeterministicMNList::UpdateMN(const CDeterministicMNCPtr& oldDmn, const CDeterministicMNStateDiff& stateDiff)
     437             : {
     438       10262 :     assert(oldDmn != nullptr);
     439       10262 :     auto oldState = oldDmn->pdmnState;
     440       20524 :     auto newState = std::make_shared<CDeterministicMNState>(*oldState);
     441       10262 :     stateDiff.ApplyToState(*newState);
     442       20524 :     UpdateMN(oldDmn, newState);
     443       10262 : }
     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       55895 : bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& _state, bool fJustCheck)
     468             : {
     469       55895 :     int nHeight = pindex->nHeight;
     470       55895 :     if (!IsDIP3Enforced(nHeight)) {
     471             :         // nothing to do
     472             :         return true;
     473             :     }
     474             : 
     475       19378 :     CDeterministicMNList oldList, newList;
     476        9689 :     CDeterministicMNListDiff diff;
     477             : 
     478        9689 :     try {
     479        9689 :         LOCK(cs);
     480             : 
     481        9689 :         if (!BuildNewListFromBlock(block, pindex->pprev, _state, newList, true)) {
     482             :             // pass the state returned by the function above
     483        1338 :             return false;
     484             :         }
     485             : 
     486        9677 :         if (fJustCheck) {
     487             :             return true;
     488             :         }
     489             : 
     490        8351 :         if (newList.GetHeight() == -1) {
     491           0 :             newList.SetHeight(nHeight);
     492             :         }
     493             : 
     494        8351 :         newList.SetBlockHash(block.GetHash());
     495             : 
     496        8351 :         oldList = GetListForBlock(pindex->pprev);
     497        8351 :         diff = oldList.BuildDiff(newList);
     498             : 
     499        8351 :         evoDb.Write(std::make_pair(DB_LIST_DIFF, newList.GetBlockHash()), diff);
     500        8351 :         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        8351 :         diff.nHeight = pindex->nHeight;
     508        8351 :         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        8351 :     if (diff.HasChanges()) {
     516        6941 :         GetMainSignals().NotifyMasternodeListChanged(false, oldList, diff);
     517        6941 :         uiInterface.NotifyMasternodeListChanged(newList);
     518             :     }
     519             : 
     520       18040 :     LOCK(cs);
     521        8351 :     CleanupCache(nHeight);
     522             : 
     523        8351 :     return true;
     524             : }
     525             : 
     526        1404 : bool CDeterministicMNManager::UndoBlock(const CBlock& block, const CBlockIndex* pindex)
     527             : {
     528        1404 :     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       41884 : void CDeterministicMNManager::SetTipIndex(const CBlockIndex* pindex)
     562             : {
     563       41884 :     LOCK(cs);
     564       41884 :     tipIndex = pindex;
     565       41884 : }
     566             : 
     567        9689 : bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state, CDeterministicMNList& mnListRet, bool debugLogs)
     568             : {
     569        9689 :     AssertLockHeld(cs);
     570        9689 :     const auto& consensus = Params().GetConsensus();
     571        9689 :     int nHeight = pindexPrev->nHeight + 1;
     572             : 
     573       19378 :     CDeterministicMNList oldList = GetListForBlock(pindexPrev);
     574        9689 :     CDeterministicMNList newList = oldList;
     575        9689 :     newList.SetBlockHash(UINT256_ZERO); // we can't know the final block hash, so better not return a (invalid) block hash
     576        9689 :     newList.SetHeight(nHeight);
     577             : 
     578       19378 :     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        9689 :     oldList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) {
     584       85724 :         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        9689 :     DecreasePoSePenalties(newList);
     599             : 
     600             :     // we skip the coinbase
     601       17938 :     for (int i = 1; i < (int)block.vtx.size(); i++) {
     602        8261 :         const CTransaction& tx = *block.vtx[i];
     603             : 
     604        8261 :         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        7775 :         } 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        7740 :         } 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        7685 :         } 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        7663 :         } else if (tx.nType == CTransaction::TxType::LLMQCOMM) {
     758        5066 :             llmq::LLMQCommPL pl;
     759        2533 :             if (!GetTxPayload(tx, pl)) {
     760           0 :                 return _state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
     761             :             }
     762        2533 :             if (!pl.commitment.IsNull()) {
     763             :                 // Double-check that the quorum index is in the active chain
     764         145 :                 const auto& params = consensus.llmqs.at((Consensus::LLMQType)pl.commitment.llmqType);
     765         145 :                 uint32_t quorumHeight = pl.nHeight - (pl.nHeight % params.dkgInterval);
     766         145 :                 auto quorumIndex = pindexPrev->GetAncestor(quorumHeight);
     767         145 :                 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         145 :                 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       17908 :     for (int i = 1; i < (int)block.vtx.size(); i++) {
     780        8231 :         const CTransaction& tx = *block.vtx[i];
     781       14004 :         for (const auto& in : tx.vin) {
     782       11546 :             auto dmn = newList.GetMNByCollateral(in.prevout);
     783        5789 :             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        9677 :     if (payee && newList.HasMN(payee->proTxHash)) {
     796       15860 :         auto newState = std::make_shared<CDeterministicMNState>(*newList.GetMN(payee->proTxHash)->pdmnState);
     797        7930 :         newState->nLastPaidHeight = nHeight;
     798       15860 :         newList.UpdateMN(payee->proTxHash, newState);
     799             :     }
     800             : 
     801        9677 :     mnListRet = std::move(newList);
     802             : 
     803        9677 :     return true;
     804             : }
     805             : 
     806         145 : 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         290 :     auto members = GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, pindexQuorum);
     811             : 
     812         580 :     for (size_t i = 0; i < members.size(); i++) {
     813         435 :         if (!mnList.HasMN(members[i]->proTxHash)) {
     814           0 :             continue;
     815             :         }
     816         435 :         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          45 :             mnList.PoSePunish(members[i]->proTxHash, mnList.CalcPenalty(66), debugLogs);
     822             :         }
     823             :     }
     824         145 : }
     825             : 
     826        9689 : void CDeterministicMNManager::DecreasePoSePenalties(CDeterministicMNList& mnList)
     827             : {
     828        9689 :     std::vector<uint256> toDecrease;
     829        9689 :     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        9689 :     mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
     833       42419 :         if (dmn->pdmnState->nPoSePenalty > 0 && dmn->pdmnState->nPoSeBanHeight == -1) {
     834        2017 :             toDecrease.emplace_back(dmn->proTxHash);
     835             :         }
     836       42419 :     });
     837             : 
     838       11706 :     for (const auto& proTxHash : toDecrease) {
     839        2017 :         mnList.PoSeDecrease(proTxHash);
     840             :     }
     841        9689 : }
     842             : 
     843      327429 : CDeterministicMNList CDeterministicMNManager::GetListForBlock(const CBlockIndex* pindex)
     844             : {
     845      654858 :     LOCK(cs);
     846             : 
     847             :     // Return early before enforcement
     848      327429 :     if (!IsDIP3Enforced(pindex->nHeight)) {
     849      240574 :         return {};
     850             :     }
     851             : 
     852       86855 :     CDeterministicMNList snapshot;
     853      173710 :     std::list<const CBlockIndex*> listDiffIndexes;
     854             : 
     855       96670 :     while (true) {
     856             :         // try using cache before reading from disk
     857       96670 :         auto itLists = mnListsCache.find(pindex->GetBlockHash());
     858       96670 :         if (itLists != mnListsCache.end()) {
     859       86853 :             snapshot = itLists->second;
     860       86855 :             break;
     861             :         }
     862             : 
     863        9817 :         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        9815 :         auto itDiffs = mnListDiffsCache.find(pindex->GetBlockHash());
     870        9815 :         if (itDiffs != mnListDiffsCache.end()) {
     871        9706 :             listDiffIndexes.emplace_front(pindex);
     872        9706 :             pindex = pindex->pprev;
     873        9706 :             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       96670 :     for (const auto& diffIndex : listDiffIndexes) {
     897        9815 :         const auto& diff = mnListDiffsCache.at(diffIndex->GetBlockHash());
     898        9815 :         if (diff.HasChanges()) {
     899        8443 :             snapshot = snapshot.ApplyDiff(diffIndex, diff);
     900             :         } else {
     901        1372 :             snapshot.SetBlockHash(diffIndex->GetBlockHash());
     902        9815 :             snapshot.SetHeight(diffIndex->nHeight);
     903             :         }
     904             :     }
     905             : 
     906       86855 :     if (tipIndex) {
     907             :         // always keep a snapshot for the tip
     908       86827 :         if (snapshot.GetBlockHash() == tipIndex->GetBlockHash()) {
     909       78463 :             mnListsCache.emplace(snapshot.GetBlockHash(), snapshot);
     910             :         } else {
     911             :             // !TODO: keep snapshots for yet alive quorums
     912             :         }
     913             :     }
     914             : 
     915       86855 :     return snapshot;
     916             : }
     917             : 
     918      287936 : CDeterministicMNList CDeterministicMNManager::GetListAtChainTip()
     919             : {
     920      575872 :     LOCK(cs);
     921      287936 :     if (!tipIndex) {
     922         320 :         return {};
     923             :     }
     924      287616 :     return GetListForBlock(tipIndex);
     925             : }
     926             : 
     927      463144 : bool CDeterministicMNManager::IsDIP3Enforced(int nHeight) const
     928             : {
     929      463144 :     return Params().GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0);
     930             : }
     931             : 
     932       33127 : bool CDeterministicMNManager::IsDIP3Enforced() const
     933             : {
     934       66254 :     int tipHeight = WITH_LOCK(cs, return tipIndex ? tipIndex->nHeight : -1;);
     935       33127 :     return IsDIP3Enforced(tipHeight);
     936             : }
     937             : 
     938      230006 : bool CDeterministicMNManager::LegacyMNObsolete(int nHeight) const
     939             : {
     940      230006 :     return nHeight > sporkManager.GetSporkValue(SPORK_21_LEGACY_MNS_MAX_HEIGHT);
     941             : }
     942             : 
     943      166256 : bool CDeterministicMNManager::LegacyMNObsolete() const
     944             : {
     945      332512 :     int tipHeight = WITH_LOCK(cs, return tipIndex ? tipIndex->nHeight : -1;);
     946      166256 :     return LegacyMNObsolete(tipHeight);
     947             : }
     948             : 
     949        8351 : void CDeterministicMNManager::CleanupCache(int nHeight)
     950             : {
     951        8351 :     AssertLockHeld(cs);
     952             : 
     953        8351 :     std::vector<uint256> toDeleteLists;
     954        8351 :     std::vector<uint256> toDeleteDiffs;
     955      859574 :     for (const auto& p : mnListsCache) {
     956      851223 :         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        8351 :     for (const auto& h : toDeleteLists) {
     963           0 :         mnListsCache.erase(h);
     964             :     }
     965      869095 :     for (const auto& p : mnListDiffsCache) {
     966      860744 :         if (p.second.nHeight + LIST_DIFFS_CACHE_SIZE < nHeight) {
     967           0 :             toDeleteDiffs.emplace_back(p.first);
     968             :         }
     969             :     }
     970        8351 :     for (const auto& h : toDeleteDiffs) {
     971           0 :         mnListDiffsCache.erase(h);
     972             :     }
     973        8351 : }
     974             : 
     975        4306 : std::vector<CDeterministicMNCPtr> CDeterministicMNManager::GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
     976             : {
     977        4306 :     auto& params = Params().GetConsensus().llmqs.at(llmqType);
     978        8612 :     auto allMns = GetListForBlock(pindexQuorum);
     979        4306 :     auto modifier = ::SerializeHash(std::make_pair(static_cast<uint8_t>(llmqType), pindexQuorum->GetBlockHash()));
     980        8612 :     return allMns.CalculateQuorum(params.size, modifier);
     981             : }
     982             : 
     983             : 

Generated by: LCOV version 1.14