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-02-23 09:33:43 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        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             : 

Generated by: LCOV version 1.14