LCOV - code coverage report
Current view: top level - src/budget - budgetmanager.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 729 904 80.6 %
Date: 2025-02-23 09:33:43 Functions: 65 69 94.2 %

          Line data    Source code
       1             : // Copyright (c) 2014-2015 The Dash developers
       2             : // Copyright (c) 2015-2022 The PIVX Core developers
       3             : // Distributed under the MIT/X11 software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "budget/budgetmanager.h"
       7             : 
       8             : #include "consensus/validation.h"
       9             : #include "evo/deterministicmns.h"
      10             : #include "masternodeman.h"
      11             : #include "netmessagemaker.h"
      12             : #include "tiertwo/tiertwo_sync_state.h"
      13             : #include "tiertwo/netfulfilledman.h"
      14             : #include "util/validation.h"
      15             : #include "validation.h"   // GetTransaction, cs_main
      16             : 
      17             : #ifdef ENABLE_WALLET
      18             : #include "wallet/wallet.h" // future: use interface instead.
      19             : #endif
      20             : 
      21             : 
      22             : #define BUDGET_ORPHAN_VOTES_CLEANUP_SECONDS (60 * 60) // One hour.
      23             : // Request type used in the net requests manager to block peers asking budget sync too often
      24             : static const std::string BUDGET_SYNC_REQUEST_RECV = "budget-sync-recv";
      25             : 
      26             : CBudgetManager g_budgetman;
      27             : 
      28             : // Used to check both proposals and finalized-budgets collateral txes
      29             : bool CheckCollateral(const uint256& nTxCollateralHash, const uint256& nExpectedHash, std::string& strError, int64_t& nTime, int nCurrentHeight, bool fBudgetFinalization);
      30             : 
      31         356 : void CBudgetManager::ReloadMapSeen()
      32             : {
      33        1068 :     const auto reloadSeenMap = [](auto& mutex1, auto& mutex2, const auto& mapBudgets, auto& mapSeen, auto& mapOrphans) {
      34        1424 :         LOCK2(mutex1, mutex2);
      35         712 :         mapSeen.clear();
      36         712 :         mapOrphans.clear();
      37         712 :         for (const auto& b : mapBudgets) {
      38           0 :             for (const auto& it : b.second.mapVotes) {
      39           0 :                 const auto& vote = it.second;
      40           0 :                 if (vote.IsValid()) {
      41           0 :                     mapSeen.emplace(vote.GetHash(), vote);
      42             :                 }
      43             :             }
      44             :         }
      45         712 :     };
      46             : 
      47         356 :     reloadSeenMap(cs_proposals, cs_votes, mapProposals, mapSeenProposalVotes, mapOrphanProposalVotes);
      48         356 :     reloadSeenMap(cs_budgets, cs_finalizedvotes, mapFinalizedBudgets, mapSeenFinalizedBudgetVotes, mapOrphanFinalizedBudgetVotes);
      49         356 : }
      50             : 
      51         177 : void CBudgetManager::CheckOrphanVotes()
      52             : {
      53         177 :     {
      54         354 :         LOCK2(cs_proposals, cs_votes);
      55         178 :         for (auto itOrphanVotes = mapOrphanProposalVotes.begin(); itOrphanVotes != mapOrphanProposalVotes.end();) {
      56           1 :             auto itProposal = mapProposals.find(itOrphanVotes->first);
      57           1 :             if (itProposal != mapProposals.end()) {
      58             :                 // Proposal found.
      59           1 :                 CBudgetProposal* bp = &(itProposal->second);
      60             :                 // Try to add orphan votes
      61           2 :                 for (const CBudgetVote& vote : itOrphanVotes->second.first) {
      62           2 :                     std::string strError;
      63           1 :                     if (!bp->AddOrUpdateVote(vote, strError)) {
      64           0 :                         LogPrint(BCLog::MNBUDGET, "Unable to add orphan vote for proposal: %s\n", strError);
      65             :                     }
      66             :                 }
      67             :                 // Remove entry from the map
      68           1 :                 itOrphanVotes = mapOrphanProposalVotes.erase(itOrphanVotes);
      69             :             } else {
      70           1 :                 ++itOrphanVotes;
      71             :             }
      72             :         }
      73             :     }
      74             : 
      75         177 :     {
      76         354 :         LOCK2(cs_budgets, cs_finalizedvotes);
      77         194 :         for (auto itOrphanVotes = mapOrphanFinalizedBudgetVotes.begin(); itOrphanVotes != mapOrphanFinalizedBudgetVotes.end();) {
      78          17 :             auto itFinalBudget = mapFinalizedBudgets.find(itOrphanVotes->first);
      79          17 :             if (itFinalBudget != mapFinalizedBudgets.end()) {
      80             :                 // Finalized budget found.
      81           1 :                 CFinalizedBudget* fb = &(itFinalBudget->second);
      82             :                 // Try to add orphan votes
      83           4 :                 for (const CFinalizedBudgetVote& vote : itOrphanVotes->second.first) {
      84           6 :                     std::string strError;
      85           3 :                     if (!fb->AddOrUpdateVote(vote, strError)) {
      86           0 :                         LogPrint(BCLog::MNBUDGET, "Unable to add orphan vote for final budget: %s\n", strError);
      87             :                     }
      88             :                 }
      89             :                 // Remove entry from the map
      90           1 :                 itOrphanVotes = mapOrphanFinalizedBudgetVotes.erase(itOrphanVotes);
      91             :             } else {
      92          17 :                 ++itOrphanVotes;
      93             :             }
      94             :         }
      95             :     }
      96             : 
      97         177 :     LogPrint(BCLog::MNBUDGET,"%s: Done\n", __func__);
      98         177 : }
      99             : 
     100           4 : uint256 CBudgetManager::SubmitFinalBudget()
     101             : {
     102           4 :     static int nSubmittedHeight = 0; // height at which final budget was submitted last time
     103           4 :     int nCurrentHeight = GetBestHeight();
     104             : 
     105           4 :     const int nBlocksPerCycle = Params().GetConsensus().nBudgetCycleBlocks;
     106           4 :     int nBlockStart = nCurrentHeight - nCurrentHeight % nBlocksPerCycle + nBlocksPerCycle;
     107           4 :     if (nSubmittedHeight >= nBlockStart){
     108           0 :         LogPrint(BCLog::MNBUDGET,"%s: nSubmittedHeight(=%ld) < nBlockStart(=%ld) condition not fulfilled.\n",
     109             :                 __func__, nSubmittedHeight, nBlockStart);
     110           0 :         return UINT256_ZERO;
     111             :     }
     112             : 
     113             :      // Submit final budget during the last 2 days (2880 blocks) before payment for Mainnet, about 9 minutes (9 blocks) for Testnet
     114           4 :     int finalizationWindow = ((nBlocksPerCycle / 30) * 2);
     115             : 
     116           4 :     if (Params().IsTestnet()) {
     117             :         // NOTE: 9 blocks for testnet is way to short to have any masternode submit an automatic vote on the finalized(!) budget,
     118             :         //       because those votes are only submitted/relayed once every 56 blocks in CFinalizedBudget::AutoCheck()
     119             : 
     120           0 :         finalizationWindow = 64; // 56 + 4 finalization confirmations + 4 minutes buffer for propagation
     121             :     }
     122             : 
     123           4 :     int nFinalizationStart = nBlockStart - finalizationWindow;
     124             : 
     125           4 :     int nOffsetToStart = nFinalizationStart - nCurrentHeight;
     126             : 
     127           4 :     if (nBlockStart - nCurrentHeight > finalizationWindow) {
     128           0 :         LogPrint(BCLog::MNBUDGET,"%s: Too early for finalization. Current block is %ld, next Superblock is %ld.\n", __func__, nCurrentHeight, nBlockStart);
     129           0 :         LogPrint(BCLog::MNBUDGET,"%s: First possible block for finalization: %ld. Last possible block for finalization: %ld. " /* Continued */
     130             :                 "You have to wait for %ld block(s) until Budget finalization will be possible\n", __func__, nFinalizationStart, nBlockStart, nOffsetToStart);
     131           0 :         return UINT256_ZERO;
     132             :     }
     133             : 
     134           4 :     std::vector<CBudgetProposal> vBudgetProposals = GetBudget();
     135           8 :     std::string strBudgetName = "main";
     136           8 :     std::vector<CTxBudgetPayment> vecTxBudgetPayments;
     137             : 
     138           8 :     for (const auto& p : vBudgetProposals) {
     139           8 :         CTxBudgetPayment txBudgetPayment;
     140           4 :         txBudgetPayment.nProposalHash = p.GetHash();
     141           4 :         txBudgetPayment.payee = p.GetPayee();
     142           4 :         txBudgetPayment.nAmount = p.GetAllotted();
     143           4 :         vecTxBudgetPayments.push_back(txBudgetPayment);
     144             :     }
     145             : 
     146           4 :     if (vecTxBudgetPayments.size() < 1) {
     147           0 :         LogPrint(BCLog::MNBUDGET,"%s: Found No Proposals For Period\n", __func__);
     148           0 :         return UINT256_ZERO;
     149             :     }
     150             : 
     151           8 :     CFinalizedBudget tempBudget(strBudgetName, nBlockStart, vecTxBudgetPayments, UINT256_ZERO);
     152           4 :     const uint256& budgetHash = tempBudget.GetHash();
     153           4 :     if (HaveFinalizedBudget(budgetHash)) {
     154           0 :         LogPrint(BCLog::MNBUDGET,"%s: Budget already exists - %s\n", __func__, budgetHash.ToString());
     155           0 :         nSubmittedHeight = nCurrentHeight;
     156           0 :         return UINT256_ZERO;
     157             :     }
     158             : 
     159             :     // See if collateral tx exists
     160           4 :     if (!mapUnconfirmedFeeTx.count(budgetHash)) {
     161             :         // create the collateral tx, send it to the network and return
     162           2 :         CTransactionRef wtx;
     163             :         // Get our change address
     164           2 :         if (vpwallets.empty() || !vpwallets[0]) {
     165           0 :             LogPrint(BCLog::MNBUDGET,"%s: Wallet not found\n", __func__);
     166           0 :             return UINT256_ZERO;
     167             :         }
     168             :         // Exit if wallet is locked
     169           2 :         if (vpwallets[0]->IsLocked()) {
     170           0 :             LogPrint(BCLog::MNBUDGET, "%s: Wallet is locked, can't make collateral transaction.\n", __func__);
     171           0 :             return UINT256_ZERO;
     172             :         }
     173           4 :         CReserveKey keyChange(vpwallets[0]);
     174           2 :         if (!vpwallets[0]->CreateBudgetFeeTX(wtx, budgetHash, keyChange, BUDGET_FEE_TX)) {
     175           0 :             LogPrint(BCLog::MNBUDGET,"%s: Can't make collateral transaction\n", __func__);
     176           0 :             return UINT256_ZERO;
     177             :         }
     178             :         // Send the tx to the network
     179           4 :         const CWallet::CommitResult& res = vpwallets[0]->CommitTransaction(wtx, keyChange, g_connman.get());
     180           2 :         if (res.status == CWallet::CommitStatus::OK) {
     181           2 :             const uint256& collateraltxid = wtx->GetHash();
     182           2 :             mapUnconfirmedFeeTx.emplace(budgetHash, collateraltxid);
     183           4 :             LogPrint(BCLog::MNBUDGET,"%s: Collateral sent. txid: %s\n", __func__, collateraltxid.ToString());
     184           2 :             return budgetHash;
     185             :         }
     186           0 :         return UINT256_ZERO;
     187             :     }
     188             : 
     189             :     // Collateral tx already exists, see if it's mature enough.
     190           4 :     CFinalizedBudget fb(strBudgetName, nBlockStart, vecTxBudgetPayments, mapUnconfirmedFeeTx.at(budgetHash));
     191           2 :     if (!AddFinalizedBudget(fb)) {
     192           0 :         return UINT256_ZERO;
     193             :     }
     194           2 :     fb.Relay();
     195           2 :     nSubmittedHeight = nCurrentHeight;
     196           4 :     LogPrint(BCLog::MNBUDGET,"%s: Done! %s\n", __func__, budgetHash.ToString());
     197           2 :     return budgetHash;
     198             : }
     199             : 
     200          14 : void CBudgetManager::SetBudgetProposalsStr(CFinalizedBudget& finalizedBudget) const
     201             : {
     202          14 :     const std::vector<uint256>& vHashes = finalizedBudget.GetProposalsHashes();
     203          28 :     std::string strProposals = "";
     204          14 :     {
     205          14 :         LOCK(cs_proposals);
     206          28 :         for (const uint256& hash: vHashes) {
     207          42 :             const std::string token = (mapProposals.count(hash) ? mapProposals.at(hash).GetName() : hash.ToString());
     208          28 :             strProposals += (strProposals == "" ? "" : ", ") + token;
     209             :         }
     210             :     }
     211          42 :     finalizedBudget.SetProposalsStr(strProposals);
     212          14 : }
     213             : 
     214          39 : std::string CBudgetManager::GetFinalizedBudgetStatus(const uint256& nHash) const
     215             : {
     216          39 :     CFinalizedBudget fb;
     217          39 :     if (!GetFinalizedBudget(nHash, fb))
     218           0 :         return strprintf("ERROR: cannot find finalized budget %s\n", nHash.ToString());
     219             : 
     220          78 :     std::string retBadHashes = "";
     221          78 :     std::string retBadPayeeOrAmount = "";
     222          39 :     int nBlockStart = fb.GetBlockStart();
     223          39 :     int nBlockEnd = fb.GetBlockEnd();
     224             : 
     225          78 :     for (int nBlockHeight = nBlockStart; nBlockHeight <= nBlockEnd; nBlockHeight++) {
     226          78 :         CTxBudgetPayment budgetPayment;
     227          39 :         if (!fb.GetBudgetPaymentByBlock(nBlockHeight, budgetPayment)) {
     228           0 :             LogPrint(BCLog::MNBUDGET,"%s: Couldn't find budget payment for block %lld\n", __func__, nBlockHeight);
     229           0 :             continue;
     230             :         }
     231             : 
     232          78 :         CBudgetProposal bp;
     233          39 :         if (!GetProposal(budgetPayment.nProposalHash, bp)) {
     234           0 :             retBadHashes += (retBadHashes == "" ? "" : ", ") + budgetPayment.nProposalHash.ToString();
     235           0 :             continue;
     236             :         }
     237             : 
     238          78 :         if (bp.GetPayee() != budgetPayment.payee || bp.GetAmount() != budgetPayment.nAmount) {
     239           0 :             retBadPayeeOrAmount += (retBadPayeeOrAmount == "" ? "" : ", ") + budgetPayment.nProposalHash.ToString();
     240             :         }
     241             :     }
     242             : 
     243          78 :     if (retBadHashes == "" && retBadPayeeOrAmount == "") return "OK";
     244             : 
     245           0 :     if (retBadHashes != "") retBadHashes = "Unknown proposal(s) hash! Check this proposal(s) before voting: " + retBadHashes;
     246           0 :     if (retBadPayeeOrAmount != "") retBadPayeeOrAmount = "Budget payee/nAmount doesn't match our proposal(s)! "+ retBadPayeeOrAmount;
     247             : 
     248           0 :     return retBadHashes + " -- " + retBadPayeeOrAmount;
     249             : }
     250             : 
     251          17 : bool CBudgetManager::AddFinalizedBudget(CFinalizedBudget& finalizedBudget, CNode* pfrom)
     252             : {
     253          17 :     AssertLockNotHeld(cs_budgets);    // need to lock cs_main here (CheckCollateral)
     254          17 :     const uint256& nHash = finalizedBudget.GetHash();
     255             : 
     256          51 :     if (WITH_LOCK(cs_budgets, return mapFinalizedBudgets.count(nHash))) {
     257           0 :         LogPrint(BCLog::MNBUDGET,"%s: finalized budget %s already added\n", __func__, nHash.ToString());
     258           0 :         return false;
     259             :     }
     260             : 
     261          17 :     if (!finalizedBudget.IsWellFormed(GetTotalBudget(finalizedBudget.GetBlockStart()))) {
     262           0 :         LogPrint(BCLog::MNBUDGET,"%s: invalid finalized budget: %s %s\n", __func__, nHash.ToString(), finalizedBudget.IsInvalidLogStr());
     263           0 :         return false;
     264             :     }
     265             : 
     266          34 :     std::string strError;
     267          17 :     int nCurrentHeight = GetBestHeight();
     268          17 :     const uint256& feeTxId = finalizedBudget.GetFeeTXHash();
     269          17 :     if (!CheckCollateral(feeTxId, nHash, strError, finalizedBudget.nTime, nCurrentHeight, true)) {
     270           0 :         LogPrint(BCLog::MNBUDGET,"%s: invalid finalized budget (%s) collateral id=%s - %s\n",
     271             :                 __func__, nHash.ToString(), feeTxId.ToString(), strError);
     272          17 :         finalizedBudget.SetStrInvalid(strError);
     273             :         return false;
     274             :     }
     275             : 
     276             :     // update expiration
     277          17 :     if (!finalizedBudget.UpdateValid(nCurrentHeight)) {
     278           0 :         LogPrint(BCLog::MNBUDGET,"%s: invalid finalized budget: %s %s\n", __func__, nHash.ToString(), finalizedBudget.IsInvalidLogStr());
     279           0 :         return false;
     280             :     }
     281             : 
     282             :     // Compare budget payments with existent proposals, don't care on the order, just verify proposals existence.
     283          17 :     std::vector<CBudgetProposal> vBudget = GetBudget();
     284          34 :     std::map<uint256, CBudgetProposal> mapWinningProposals;
     285          32 :     for (const CBudgetProposal& p: vBudget) { mapWinningProposals.emplace(p.GetHash(), p); }
     286          17 :     if (!finalizedBudget.CheckProposals(mapWinningProposals)) {
     287           6 :         finalizedBudget.SetStrInvalid("Invalid proposals");
     288           3 :         LogPrint(BCLog::MNBUDGET,"%s: Budget finalization does not match with winning proposals\n", __func__);
     289             :         // just for now (until v6), request proposals and budget sync in case we are missing them
     290           3 :         if (pfrom) {
     291           2 :             CNetMsgMaker maker(pfrom->GetSendVersion());
     292             :             // First, request single proposals that we don't have.
     293           4 :             for (const auto& propId : finalizedBudget.GetProposalsHashes()) {
     294           2 :                 if (!g_budgetman.HaveProposal(propId)) {
     295           1 :                     g_connman->PushMessage(pfrom, maker.Make(NetMsgType::BUDGETVOTESYNC, propId));
     296             :                 }
     297             :             }
     298             : 
     299             :             // Second a full budget sync for missing votes and the budget finalization that we are rejecting here.
     300             :             // Note: this will not make any effect on peers with version <= 70923 as they, invalidly, are blocking
     301             :             // follow-up budget sync request for the entire node life cycle.
     302           2 :             uint256 n;
     303           2 :             g_connman->PushMessage(pfrom, maker.Make(NetMsgType::BUDGETVOTESYNC, n));
     304             :         }
     305           3 :         return false;
     306             :     }
     307             : 
     308             :     // Add budget finalization.
     309          14 :     SetBudgetProposalsStr(finalizedBudget);
     310          14 :     ForceAddFinalizedBudget(nHash, feeTxId, finalizedBudget);
     311             : 
     312          56 :     LogPrint(BCLog::MNBUDGET,"%s: finalized budget %s [%s (%s)] added\n",
     313             :             __func__, nHash.ToString(), finalizedBudget.GetName(), finalizedBudget.GetProposalsStr());
     314             :     return true;
     315             : }
     316             : 
     317          19 : void CBudgetManager::ForceAddFinalizedBudget(const uint256& nHash, const uint256& feeTxId, const CFinalizedBudget& finalizedBudget)
     318             : {
     319          19 :     LOCK(cs_budgets);
     320          19 :     mapFinalizedBudgets.emplace(nHash, finalizedBudget);
     321             :     // Add to feeTx index
     322          19 :     mapFeeTxToBudget.emplace(feeTxId, nHash);
     323             :     // Remove the budget from the unconfirmed map, if it was there
     324          36 :     if (mapUnconfirmedFeeTx.count(nHash))
     325          19 :         mapUnconfirmedFeeTx.erase(nHash);
     326          19 : }
     327             : 
     328         184 : bool CBudgetManager::AddProposal(CBudgetProposal& budgetProposal)
     329             : {
     330         184 :     AssertLockNotHeld(cs_proposals);    // need to lock cs_main here (CheckCollateral)
     331         184 :     const uint256& nHash = budgetProposal.GetHash();
     332             : 
     333         552 :     if (WITH_LOCK(cs_proposals, return mapProposals.count(nHash))) {
     334           0 :         LogPrint(BCLog::MNBUDGET,"%s: proposal %s already added\n", __func__, nHash.ToString());
     335           0 :         return false;
     336             :     }
     337             : 
     338         184 :     if (!budgetProposal.IsWellFormed(GetTotalBudget(budgetProposal.GetBlockStart()))) {
     339           0 :         LogPrint(BCLog::MNBUDGET,"%s: Invalid budget proposal %s %s\n", __func__, nHash.ToString(), budgetProposal.IsInvalidLogStr());
     340           0 :         return false;
     341             :     }
     342             : 
     343         368 :     std::string strError;
     344         184 :     int nCurrentHeight = GetBestHeight();
     345         184 :     const uint256& feeTxId = budgetProposal.GetFeeTXHash();
     346         184 :     if (!CheckCollateral(feeTxId, nHash, strError, budgetProposal.nTime, nCurrentHeight, false)) {
     347           0 :         LogPrint(BCLog::MNBUDGET,"%s: invalid budget proposal (%s) collateral id=%s - %s\n",
     348             :                 __func__, nHash.ToString(), feeTxId.ToString(), strError);
     349         184 :         budgetProposal.SetStrInvalid(strError);
     350             :         return false;
     351             :     }
     352             : 
     353             :     // update expiration / heavily-downvoted
     354         184 :     int mnCount = mnodeman.CountEnabled();
     355         184 :     if (!budgetProposal.UpdateValid(nCurrentHeight, mnCount)) {
     356           0 :         LogPrint(BCLog::MNBUDGET,"%s: Invalid budget proposal %s %s\n", __func__, nHash.ToString(), budgetProposal.IsInvalidLogStr());
     357           0 :         return false;
     358             :     }
     359             : 
     360         184 :     {
     361         184 :         LOCK(cs_proposals);
     362         184 :         mapProposals.emplace(nHash, budgetProposal);
     363             :         // Add to feeTx index
     364         184 :         mapFeeTxToProposal.emplace(feeTxId, nHash);
     365             :     }
     366         552 :     LogPrint(BCLog::MNBUDGET,"%s: budget proposal %s [%s] added\n", __func__, nHash.ToString(), budgetProposal.GetName());
     367             : 
     368             :     return true;
     369             : }
     370             : 
     371        1167 : void CBudgetManager::CheckAndRemove()
     372             : {
     373        1167 :     int nCurrentHeight = GetBestHeight();
     374        1167 :     std::map<uint256, CFinalizedBudget> tmpMapFinalizedBudgets;
     375        1167 :     std::map<uint256, CBudgetProposal> tmpMapProposals;
     376             : 
     377             :     // Get MN count, used for the heavily down-voted check
     378        1167 :     int mnCount = mnodeman.CountEnabled();
     379             : 
     380             :     // Check Proposals first
     381        1167 :     {
     382        1167 :         LOCK(cs_proposals);
     383        1167 :         LogPrint(BCLog::MNBUDGET, "%s: mapProposals cleanup - size before: %d\n", __func__, mapProposals.size());
     384        2620 :         for (auto& it: mapProposals) {
     385        1453 :             CBudgetProposal* pbudgetProposal = &(it.second);
     386        1453 :             if (!pbudgetProposal->UpdateValid(nCurrentHeight, mnCount)) {
     387           0 :                 LogPrint(BCLog::MNBUDGET,"%s: Invalid budget proposal %s %s\n", __func__, (it.first).ToString(), pbudgetProposal->IsInvalidLogStr());
     388        1453 :                 mapFeeTxToProposal.erase(pbudgetProposal->GetFeeTXHash());
     389             :             } else {
     390        5812 :                  LogPrint(BCLog::MNBUDGET,"%s: Found valid budget proposal: %s %s\n", __func__,
     391             :                           pbudgetProposal->GetName(), pbudgetProposal->GetFeeTXHash().ToString());
     392        2906 :                  tmpMapProposals.emplace(pbudgetProposal->GetHash(), *pbudgetProposal);
     393             :             }
     394             :         }
     395             :         // Remove invalid entries by overwriting complete map
     396        1167 :         mapProposals.swap(tmpMapProposals);
     397        1167 :         LogPrint(BCLog::MNBUDGET, "%s: mapProposals cleanup - size after: %d\n", __func__, mapProposals.size());
     398             :     }
     399             : 
     400             :     // Then check finalized budgets
     401        1167 :     {
     402        1167 :         LOCK(cs_budgets);
     403        1167 :         LogPrint(BCLog::MNBUDGET, "%s: mapFinalizedBudgets cleanup - size before: %d\n", __func__, mapFinalizedBudgets.size());
     404        1256 :         for (auto& it: mapFinalizedBudgets) {
     405          89 :             CFinalizedBudget* pfinalizedBudget = &(it.second);
     406          89 :             if (!pfinalizedBudget->UpdateValid(nCurrentHeight)) {
     407           3 :                 LogPrint(BCLog::MNBUDGET,"%s: Invalid finalized budget %s %s\n", __func__, (it.first).ToString(), pfinalizedBudget->IsInvalidLogStr());
     408          89 :                 mapFeeTxToBudget.erase(pfinalizedBudget->GetFeeTXHash());
     409             :             } else {
     410         352 :                 LogPrint(BCLog::MNBUDGET,"%s: Found valid finalized budget: %s %s\n", __func__,
     411             :                           pfinalizedBudget->GetName(), pfinalizedBudget->GetFeeTXHash().ToString());
     412         176 :                 tmpMapFinalizedBudgets.emplace(pfinalizedBudget->GetHash(), *pfinalizedBudget);
     413             :             }
     414             :         }
     415             :         // Remove invalid entries by overwriting complete map
     416        1167 :         mapFinalizedBudgets = tmpMapFinalizedBudgets;
     417        1167 :         LogPrint(BCLog::MNBUDGET, "%s: mapFinalizedBudgets cleanup - size after: %d\n", __func__, mapFinalizedBudgets.size());
     418             :     }
     419             :     // Masternodes vote on valid ones
     420        1167 :     VoteOnFinalizedBudgets();
     421        1167 : }
     422             : 
     423      111345 : void CBudgetManager::RemoveByFeeTxId(const uint256& feeTxId)
     424             : {
     425      111345 :     {
     426      111345 :         LOCK(cs_proposals);
     427             :         // Is this collateral related to a proposal?
     428      111345 :         const auto& it = mapFeeTxToProposal.find(feeTxId);
     429      111345 :         if (it != mapFeeTxToProposal.end()) {
     430             :             // Remove proposal
     431           0 :             CBudgetProposal* p = FindProposal(it->second);
     432           0 :             if (p) {
     433           0 :                 LogPrintf("%s: Removing proposal %s (collateral disconnected, id=%s)\n", __func__, p->GetName(), feeTxId.ToString());
     434           0 :                 {
     435             :                     // Erase seen/orphan votes
     436           0 :                     LOCK(cs_votes);
     437           0 :                     for (const auto& vote: p->GetVotes()) {
     438           0 :                         const uint256& hash{vote.second.GetHash()};
     439           0 :                         mapSeenProposalVotes.erase(hash);
     440           0 :                         mapOrphanProposalVotes.erase(hash);
     441             :                     }
     442             :                 }
     443             :                 // Erase proposal object
     444           0 :                 mapProposals.erase(it->second);
     445             :             }
     446             :             // Remove from collateral index
     447           0 :             mapFeeTxToProposal.erase(it);
     448           0 :             return;
     449             :         }
     450             :     }
     451      111345 :     {
     452      222690 :         LOCK(cs_budgets);
     453             :         // Is this collateral related to a finalized budget?
     454      111345 :         const auto& it = mapFeeTxToBudget.find(feeTxId);
     455      111345 :         if (it != mapFeeTxToBudget.end()) {
     456             :             // Remove finalized budget
     457           0 :             CFinalizedBudget* b = FindFinalizedBudget(it->second);
     458           0 :             if (b) {
     459           0 :                 LogPrintf("%s: Removing finalized budget %s (collateral disconnected, id=%s)\n", __func__, b->GetName(), feeTxId.ToString());
     460           0 :                 {
     461             :                     // Erase seen/orphan votes
     462           0 :                     LOCK(cs_finalizedvotes);
     463           0 :                     for (const uint256& hash: b->GetVotesHashes()) {
     464           0 :                         mapSeenFinalizedBudgetVotes.erase(hash);
     465           0 :                         mapOrphanFinalizedBudgetVotes.erase(hash);
     466             :                     }
     467             :                 }
     468             :                 // Erase finalized budget object
     469           0 :                 mapFinalizedBudgets.erase(it->second);
     470             :             }
     471             :             // Remove from collateral index
     472           0 :             mapFeeTxToBudget.erase(it);
     473             :         }
     474             :     }
     475             : }
     476             : 
     477        5756 : CBudgetManager::HighestFinBudget CBudgetManager::GetBudgetWithHighestVoteCount(int chainHeight) const
     478             : {
     479        5756 :     LOCK(cs_budgets);
     480        5756 :     int highestVoteCount = 0;
     481        5756 :     const CFinalizedBudget* pHighestBudget = nullptr;
     482       10847 :     for (const auto& it: mapFinalizedBudgets) {
     483        5091 :         const CFinalizedBudget* pfinalizedBudget = &(it.second);
     484        5091 :         int voteCount = pfinalizedBudget->GetVoteCount();
     485        9995 :         if (voteCount > highestVoteCount &&
     486        5091 :             chainHeight >= pfinalizedBudget->GetBlockStart() &&
     487        3417 :             chainHeight <= pfinalizedBudget->GetBlockEnd()) {
     488             :             pHighestBudget = pfinalizedBudget;
     489             :             highestVoteCount = voteCount;
     490             :         }
     491             :     }
     492       11512 :     return {pHighestBudget, highestVoteCount};
     493             : }
     494             : 
     495        5689 : int CBudgetManager::GetHighestVoteCount(int chainHeight) const
     496             : {
     497        5689 :     const auto& highestBudFin = GetBudgetWithHighestVoteCount(chainHeight);
     498        5689 :     return (highestBudFin.m_budget_fin ? highestBudFin.m_vote_count : -1);
     499             : }
     500             : 
     501        2941 : bool CBudgetManager::GetPayeeAndAmount(int chainHeight, CScript& payeeRet, CAmount& nAmountRet) const
     502             : {
     503        2941 :     int nCountThreshold;
     504        2941 :     if (!IsBudgetPaymentBlock(chainHeight, nCountThreshold))
     505             :         return false;
     506             : 
     507          40 :     const auto& highestBudFin = GetBudgetWithHighestVoteCount(chainHeight);
     508          40 :     const CFinalizedBudget* pfb = highestBudFin.m_budget_fin;
     509          40 :     return pfb && pfb->GetPayeeAndAmount(chainHeight, payeeRet, nAmountRet) && highestBudFin.m_vote_count > nCountThreshold;
     510             : }
     511             : 
     512        2383 : bool CBudgetManager::GetExpectedPayeeAmount(int chainHeight, CAmount& nAmountRet) const
     513             : {
     514        2383 :     CScript payeeRet;
     515        2383 :     return GetPayeeAndAmount(chainHeight, payeeRet, nAmountRet);
     516             : }
     517             : 
     518         558 : bool CBudgetManager::FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const
     519             : {
     520         558 :     if (nHeight <= 0) return false;
     521             : 
     522        1116 :     CScript payee;
     523         558 :     CAmount nAmount = 0;
     524             : 
     525         558 :     if (!GetPayeeAndAmount(nHeight, payee, nAmount))
     526             :         return false;
     527             : 
     528           8 :     CAmount blockValue = GetBlockValue(nHeight);
     529             : 
     530             :     // Starting from PIVX v6.0 masternode and budgets are paid in the coinbase tx of PoS blocks
     531          11 :     const bool fPayCoinstake = fProofOfStake &&
     532           3 :                                !Params().GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0);
     533             : 
     534           8 :     if (fProofOfStake) {
     535           3 :         if (fPayCoinstake) {
     536           2 :             unsigned int i = txCoinstake.vout.size();
     537           2 :             txCoinstake.vout.resize(i + 1);
     538           2 :             txCoinstake.vout[i].scriptPubKey = payee;
     539           2 :             txCoinstake.vout[i].nValue = nAmount;
     540             :         } else {
     541           1 :             txCoinbase.vout.resize(1);
     542           1 :             txCoinbase.vout[0].scriptPubKey = payee;
     543           1 :             txCoinbase.vout[0].nValue = nAmount;
     544             :         }
     545             :     } else {
     546             :         //miners get the full amount on these blocks
     547           5 :         txCoinbase.vout[0].nValue = blockValue;
     548           5 :         txCoinbase.vout.resize(2);
     549             : 
     550             :         //these are super blocks, so their value can be much larger than normal
     551           5 :         txCoinbase.vout[1].scriptPubKey = payee;
     552           5 :         txCoinbase.vout[1].nValue = nAmount;
     553             :     }
     554             : 
     555         566 :     CTxDestination address;
     556           8 :     ExtractDestination(payee, address);
     557          11 :     LogPrint(BCLog::MNBUDGET,"%s: Budget payment to %s for %lld\n", __func__, EncodeDestination(address), nAmount);
     558           8 :     return true;
     559             : }
     560             : 
     561        1167 : void CBudgetManager::VoteOnFinalizedBudgets()
     562             : {
     563             :     // function called only from initialized masternodes
     564        1167 :     if (!fMasterNode) {
     565         773 :         LogPrint(BCLog::MNBUDGET,"%s: Not a masternode\n", __func__);
     566        1163 :         return;
     567             :     }
     568             : 
     569             :     // Do this 1 in 4 blocks -- spread out the voting activity
     570             :     // -- this function is only called every fourteenth block, so this is really 1 in 56 blocks
     571         394 :     if (GetRandInt(4) != 0) {
     572         296 :         LogPrint(BCLog::MNBUDGET,"%s: waiting\n", __func__);
     573         296 :         return;
     574             :     }
     575             : 
     576             :     // Get the active masternode (operator) key
     577         102 :     CTxIn mnVin;
     578         102 :     Optional<CKey> mnKey{nullopt};
     579         102 :     CBLSSecretKey blsKey;
     580          98 :     if (!GetActiveMasternodeKeys(mnVin, mnKey, blsKey)) {
     581          94 :         return;
     582             :     }
     583             : 
     584          95 :     std::vector<CBudgetProposal> vBudget = GetBudget();
     585          91 :     if (vBudget.empty()) {
     586          87 :         LogPrint(BCLog::MNBUDGET,"%s: No proposal can be finalized\n", __func__);
     587          87 :         return;
     588             :     }
     589             : 
     590           8 :     std::map<uint256, CBudgetProposal> mapWinningProposals;
     591           8 :     for (const CBudgetProposal& p: vBudget) {
     592           8 :         mapWinningProposals.emplace(p.GetHash(), p);
     593             :     }
     594             :     // Vector containing the hash of finalized budgets to sign
     595           8 :     std::vector<uint256> vBudgetHashes;
     596           4 :     {
     597           4 :         LOCK(cs_budgets);
     598           6 :         for (auto& it: mapFinalizedBudgets) {
     599           2 :             CFinalizedBudget* pfb = &(it.second);
     600             :             // we only need to check this once
     601           2 :             if (pfb->IsAutoChecked()) continue;
     602           1 :             pfb->SetAutoChecked(true);
     603             :             //only vote for exact matches
     604           1 :             if (strBudgetMode == "auto") {
     605             :                 // compare budget payments with winning proposals
     606           1 :                 if (!pfb->CheckProposals(mapWinningProposals)) {
     607           0 :                     continue;
     608             :                 }
     609             :             }
     610             :             // exact match found. add budget hash to sign it later.
     611           1 :             vBudgetHashes.emplace_back(pfb->GetHash());
     612             :         }
     613             :     }
     614             : 
     615             :     // Sign finalized budgets
     616           5 :     for (const uint256& budgetHash: vBudgetHashes) {
     617           1 :         CFinalizedBudgetVote vote(mnVin, budgetHash);
     618           1 :         if (mnKey != nullopt) {
     619             :             // Legacy MN
     620           0 :             if (!vote.Sign(*mnKey, mnKey->GetPubKey().GetID())) {
     621           0 :                 LogPrintf("%s: Failure to sign budget %s\n", __func__, budgetHash.ToString());
     622           0 :                 continue;
     623             :             }
     624             :         } else {
     625             :             // DMN
     626           1 :             if (!vote.Sign(blsKey)) {
     627           0 :                 LogPrintf("%s: Failure to sign budget %s with DMN\n", __func__, budgetHash.ToString());
     628           0 :                 continue;
     629             :             }
     630             :         }
     631           1 :         std::string strError = "";
     632           1 :         if (!UpdateFinalizedBudget(vote, nullptr, strError)) {
     633           1 :             LogPrintf("%s: Error submitting vote - %s\n", __func__, strError);
     634           2 :             continue;
     635             :         }
     636           0 :         LogPrint(BCLog::MNBUDGET, "%s: new finalized budget vote signed: %s\n", __func__, vote.GetHash().ToString());
     637           0 :         AddSeenFinalizedBudgetVote(vote);
     638           0 :         vote.Relay();
     639             :     }
     640             : }
     641             : 
     642           0 : CFinalizedBudget* CBudgetManager::FindFinalizedBudget(const uint256& nHash)
     643             : {
     644           0 :     AssertLockHeld(cs_budgets);
     645           0 :     auto it = mapFinalizedBudgets.find(nHash);
     646           0 :     return it != mapFinalizedBudgets.end() ? &(it->second) : nullptr;
     647             : }
     648             : 
     649         157 : const CBudgetProposal* CBudgetManager::FindProposalByName(const std::string& strProposalName) const
     650             : {
     651         157 :     LOCK(cs_proposals);
     652             : 
     653         157 :     int64_t nYesCountMax = std::numeric_limits<int64_t>::min();
     654         157 :     const CBudgetProposal* pbudgetProposal = nullptr;
     655             : 
     656        2504 :     for (const auto& it: mapProposals) {
     657        2347 :         const CBudgetProposal& proposal = it.second;
     658        2347 :         int64_t nYesCount = proposal.GetYeas() - proposal.GetNays();
     659        7041 :         if (proposal.GetName() == strProposalName && nYesCount > nYesCountMax) {
     660         157 :             pbudgetProposal = &proposal;
     661         157 :             nYesCountMax = nYesCount;
     662             :         }
     663             :     }
     664             : 
     665         314 :     return pbudgetProposal;
     666             : }
     667             : 
     668           0 : CBudgetProposal* CBudgetManager::FindProposal(const uint256& nHash)
     669             : {
     670           0 :     AssertLockHeld(cs_proposals);
     671           0 :     auto it = mapProposals.find(nHash);
     672           0 :     return it != mapProposals.end() ? &(it->second) : nullptr;
     673             : }
     674             : 
     675          39 : bool CBudgetManager::GetProposal(const uint256& nHash, CBudgetProposal& bp) const
     676             : {
     677          78 :     LOCK(cs_proposals);
     678          39 :     auto it = mapProposals.find(nHash);
     679          39 :     if (it == mapProposals.end()) return false;
     680          39 :     bp = it->second;
     681             :     return true;
     682             : }
     683             : 
     684          39 : bool CBudgetManager::GetFinalizedBudget(const uint256& nHash, CFinalizedBudget& fb) const
     685             : {
     686          78 :     LOCK(cs_budgets);
     687          39 :     auto it = mapFinalizedBudgets.find(nHash);
     688          39 :     if (it == mapFinalizedBudgets.end()) return false;
     689          39 :     fb = it->second;
     690             :     return true;
     691             : }
     692             : 
     693        5689 : bool CBudgetManager::IsBudgetPaymentBlock(int nBlockHeight, int& nCountThreshold) const
     694             : {
     695        5689 :     int nHighestCount = GetHighestVoteCount(nBlockHeight);
     696        5689 :     int nCountEnabled = mnodeman.CountEnabled();
     697        5689 :     int nFivePercent = nCountEnabled / 20;
     698             :     // threshold for highest finalized budgets (highest vote count - 10% of active masternodes)
     699        5689 :     nCountThreshold = nHighestCount - (nCountEnabled / 10);
     700             :     // reduce the threshold if there are less than 10 enabled masternodes
     701        5689 :     if (nCountThreshold == nHighestCount) nCountThreshold--;
     702             : 
     703        5689 :     LogPrint(BCLog::MNBUDGET,"%s: nHighestCount: %lli, 5%% of Masternodes: %lli.\n",
     704             :             __func__, nHighestCount, nFivePercent);
     705             : 
     706             :     // If budget doesn't have 5% of the network votes, then we should pay a masternode instead
     707        5689 :     return (nHighestCount > nFivePercent);
     708             : }
     709             : 
     710        2721 : bool CBudgetManager::IsBudgetPaymentBlock(int nBlockHeight) const
     711             : {
     712        2721 :     int nCountThreshold;
     713        2721 :     return IsBudgetPaymentBlock(nBlockHeight, nCountThreshold);
     714             : }
     715             : 
     716          27 : TrxValidationStatus CBudgetManager::IsTransactionValid(const CTransaction& txNew, const uint256& nBlockHash, int nBlockHeight) const
     717             : {
     718          27 :     int nCountThreshold = 0;
     719          27 :     if (!IsBudgetPaymentBlock(nBlockHeight, nCountThreshold)) {
     720             :         // If budget doesn't have 5% of the network votes, then we should pay a masternode instead
     721             :         return TrxValidationStatus::InValid;
     722             :     }
     723             : 
     724             :     // check the highest finalized budgets (- 10% to assist in consensus)
     725          27 :     bool fThreshold = false;
     726          27 :     {
     727          27 :         LOCK(cs_budgets);
     728             :         // Get the finalized budget with the highest amount of votes..
     729          27 :         const auto& highestBudFin = GetBudgetWithHighestVoteCount(nBlockHeight);
     730          27 :         const CFinalizedBudget* highestVotesBudget = highestBudFin.m_budget_fin;
     731          27 :         if (highestVotesBudget) {
     732             :             // Need to surpass the threshold
     733          27 :             if (highestBudFin.m_vote_count > nCountThreshold) {
     734          27 :                 fThreshold = true;
     735          27 :                 if (highestVotesBudget->IsTransactionValid(txNew, nBlockHash, nBlockHeight) ==
     736             :                     TrxValidationStatus::Valid) {
     737          48 :                     return TrxValidationStatus::Valid;
     738             :                 }
     739             :             }
     740             :             // tx not valid
     741           3 :             LogPrint(BCLog::MNBUDGET, "%s: ignoring budget. Out of range or tx not valid.\n", __func__);
     742             :         }
     743             :     }
     744             : 
     745             :     // If not enough masternodes autovoted for any of the finalized budgets or if none of the txs
     746             :     // are valid, we should pay a masternode instead
     747           3 :     return fThreshold ? TrxValidationStatus::InValid : TrxValidationStatus::VoteThreshold;
     748             : }
     749             : 
     750         164 : std::vector<CBudgetProposal*> CBudgetManager::GetAllProposalsOrdered()
     751             : {
     752         164 :     LOCK(cs_proposals);
     753         164 :     std::vector<CBudgetProposal*> vBudgetProposalRet;
     754        1087 :     for (auto& it: mapProposals) {
     755         923 :         CBudgetProposal* pbudgetProposal = &(it.second);
     756         923 :         RemoveStaleVotesOnProposal(pbudgetProposal);
     757         923 :         vBudgetProposalRet.push_back(pbudgetProposal);
     758             :     }
     759         164 :     std::sort(vBudgetProposalRet.begin(), vBudgetProposalRet.end(), CBudgetProposal::PtrHigherYes);
     760         328 :     return vBudgetProposalRet;
     761             : }
     762             : 
     763         136 : std::vector<CBudgetProposal> CBudgetManager::GetBudget()
     764             : {
     765         272 :     LOCK(cs_proposals);
     766             : 
     767         136 :     int nHeight = GetBestHeight();
     768         136 :     if (nHeight <= 0)
     769           0 :         return {};
     770             : 
     771             :     // ------- Get proposals ordered by votes (highest to lowest)
     772         272 :     std::vector<CBudgetProposal*> vProposalsOrdered = GetAllProposalsOrdered();
     773             : 
     774             :     // ------- Grab The Budgets In Order
     775         272 :     std::vector<CBudgetProposal> vBudgetProposalsRet;
     776         136 :     CAmount nBudgetAllocated = 0;
     777             : 
     778         136 :     const int nBlocksPerCycle = Params().GetConsensus().nBudgetCycleBlocks;
     779         136 :     int nBlockStart = nHeight - nHeight % nBlocksPerCycle + nBlocksPerCycle;
     780         136 :     int nBlockEnd = nBlockStart + nBlocksPerCycle - 1;
     781         136 :     int mnCount = mnodeman.CountEnabled();
     782         136 :     CAmount nTotalBudget = GetTotalBudget(nBlockStart);
     783             : 
     784         675 :     for (CBudgetProposal* pbudgetProposal: vProposalsOrdered) {
     785        1617 :         LogPrint(BCLog::MNBUDGET,"%s: Processing Budget %s\n", __func__, pbudgetProposal->GetName());
     786             :         //prop start/end should be inside this period
     787         539 :         if (pbudgetProposal->IsPassing(nBlockStart, nBlockEnd, mnCount)) {
     788          43 :             LogPrint(BCLog::MNBUDGET,"%s:  -   Check 1 passed: valid=%d | %ld <= %ld | %ld >= %ld | Yeas=%d Nays=%d Count=%d | established=%d\n",
     789             :                     __func__, pbudgetProposal->IsValid(), pbudgetProposal->GetBlockStart(), nBlockStart, pbudgetProposal->GetBlockEnd(),
     790             :                     nBlockEnd, pbudgetProposal->GetYeas(), pbudgetProposal->GetNays(), mnCount / 10, pbudgetProposal->IsEstablished());
     791             : 
     792          43 :             if (pbudgetProposal->GetAmount() + nBudgetAllocated <= nTotalBudget) {
     793          43 :                 pbudgetProposal->SetAllotted(pbudgetProposal->GetAmount());
     794          43 :                 nBudgetAllocated += pbudgetProposal->GetAmount();
     795          43 :                 vBudgetProposalsRet.emplace_back(*pbudgetProposal);
     796          43 :                 LogPrint(BCLog::MNBUDGET,"%s:  -     Check 2 passed: Budget added\n", __func__);
     797             :             } else {
     798           0 :                 pbudgetProposal->SetAllotted(0);
     799           0 :                 LogPrint(BCLog::MNBUDGET,"%s:  -     Check 2 failed: no amount allotted\n", __func__);
     800             :             }
     801             : 
     802             :         } else {
     803         496 :             LogPrint(BCLog::MNBUDGET,"%s:  -   Check 1 failed: valid=%d | %ld <= %ld | %ld >= %ld | Yeas=%d Nays=%d Count=%d | established=%d\n",
     804             :                     __func__, pbudgetProposal->IsValid(), pbudgetProposal->GetBlockStart(), nBlockStart, pbudgetProposal->GetBlockEnd(),
     805             :                     nBlockEnd, pbudgetProposal->GetYeas(), pbudgetProposal->GetNays(), mnodeman.CountEnabled() / 10,
     806             :                     pbudgetProposal->IsEstablished());
     807             :         }
     808             : 
     809             :     }
     810             : 
     811         136 :     return vBudgetProposalsRet;
     812             : }
     813             : 
     814          43 : std::vector<CFinalizedBudget*> CBudgetManager::GetFinalizedBudgets()
     815             : {
     816          43 :     LOCK(cs_budgets);
     817             : 
     818          43 :     std::vector<CFinalizedBudget*> vFinalizedBudgetsRet;
     819             : 
     820             :     // ------- Grab The Budgets In Order
     821          84 :     for (auto& it: mapFinalizedBudgets) {
     822          41 :         vFinalizedBudgetsRet.push_back(&(it.second));
     823             :     }
     824          43 :     std::sort(vFinalizedBudgetsRet.begin(), vFinalizedBudgetsRet.end(), CFinalizedBudget::PtrGreater);
     825             : 
     826          86 :     return vFinalizedBudgetsRet;
     827             : }
     828             : 
     829           0 : std::string CBudgetManager::GetRequiredPaymentsString(int nBlockHeight)
     830             : {
     831           0 :     LOCK(cs_budgets);
     832             : 
     833           0 :     std::string ret = "unknown-budget";
     834             : 
     835           0 :     std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
     836           0 :     while (it != mapFinalizedBudgets.end()) {
     837           0 :         CFinalizedBudget* pfinalizedBudget = &((*it).second);
     838           0 :         if (nBlockHeight >= pfinalizedBudget->GetBlockStart() && nBlockHeight <= pfinalizedBudget->GetBlockEnd()) {
     839           0 :             CTxBudgetPayment payment;
     840           0 :             if (pfinalizedBudget->GetBudgetPaymentByBlock(nBlockHeight, payment)) {
     841           0 :                 if (ret == "unknown-budget") {
     842           0 :                     ret = payment.nProposalHash.ToString();
     843             :                 } else {
     844           0 :                     ret += ",";
     845           0 :                     ret += payment.nProposalHash.ToString();
     846             :                 }
     847             :             } else {
     848           0 :                 LogPrint(BCLog::MNBUDGET,"%s:  Couldn't find budget payment for block %d\n", __func__, nBlockHeight);
     849             :             }
     850             :         }
     851             : 
     852           0 :         ++it;
     853             :     }
     854             : 
     855           0 :     return ret;
     856             : }
     857             : 
     858       29647 : CAmount CBudgetManager::GetTotalBudget(int nHeight)
     859             : {
     860             :     // 100% of block reward after V5.5 upgrade
     861       29647 :     CAmount nSubsidy = GetBlockValue(nHeight);
     862             : 
     863             :     // 20% of block reward prior to V5.5 upgrade
     864       29647 :     if (nHeight <= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_V5_5].nActivationHeight) {
     865       25900 :         nSubsidy /= 5;
     866             :     }
     867             : 
     868             :     // multiplied by the number of blocks in a cycle (144 on testnet, 30*1440 on mainnet)
     869       29647 :     return nSubsidy * Params().GetConsensus().nBudgetCycleBlocks;
     870             : }
     871             : 
     872          45 : void CBudgetManager::AddSeenProposalVote(const CBudgetVote& vote)
     873             : {
     874          45 :     LOCK(cs_votes);
     875          90 :     mapSeenProposalVotes.emplace(vote.GetHash(), vote);
     876          45 : }
     877             : 
     878          36 : void CBudgetManager::AddSeenFinalizedBudgetVote(const CFinalizedBudgetVote& vote)
     879             : {
     880          36 :     LOCK(cs_finalizedvotes);
     881          72 :     mapSeenFinalizedBudgetVotes.emplace(vote.GetHash(), vote);
     882          36 : }
     883             : 
     884        2344 : void CBudgetManager::RemoveStaleVotesOnProposal(CBudgetProposal* prop)
     885             : {
     886        2344 :     AssertLockHeld(cs_proposals);
     887        4688 :     LogPrint(BCLog::MNBUDGET, "Cleaning proposal votes for %s. Before: YES=%d, NO=%d\n",
     888             :             prop->GetName(), prop->GetYeas(), prop->GetNays());
     889             : 
     890        2344 :     auto it = prop->mapVotes.begin();
     891        2806 :     while (it != prop->mapVotes.end()) {
     892         462 :         auto mnList = deterministicMNManager->GetListAtChainTip();
     893         924 :         auto dmn = mnList.GetMNByCollateral(it->first);
     894         462 :         if (dmn) {
     895          86 :             (*it).second.SetValid(!dmn->IsPoSeBanned());
     896             :         } else {
     897             :             // -- Legacy System (!TODO: remove after enforcement) --
     898         376 :             CMasternode* pmn = mnodeman.Find(it->first);
     899         639 :             (*it).second.SetValid(pmn && pmn->IsEnabled());
     900             :         }
     901         462 :         ++it;
     902             :     }
     903             : 
     904        4688 :     LogPrint(BCLog::MNBUDGET, "Cleaned proposal votes for %s. After: YES=%d, NO=%d\n",
     905             :             prop->GetName(), prop->GetYeas(), prop->GetNays());
     906        2344 : }
     907             : 
     908          87 : void CBudgetManager::RemoveStaleVotesOnFinalBudget(CFinalizedBudget* fbud)
     909             : {
     910          87 :     AssertLockHeld(cs_budgets);
     911         261 :     LogPrint(BCLog::MNBUDGET, "Cleaning finalized budget votes for [%s (%s)]. Before: %d\n",
     912             :             fbud->GetName(), fbud->GetProposalsStr(), fbud->GetVoteCount());
     913             : 
     914          87 :     auto it = fbud->mapVotes.begin();
     915         344 :     while (it != fbud->mapVotes.end()) {
     916         257 :         auto mnList = deterministicMNManager->GetListAtChainTip();
     917         514 :         auto dmn = mnList.GetMNByCollateral(it->first);
     918         257 :         if (dmn) {
     919          29 :             (*it).second.SetValid(!dmn->IsPoSeBanned());
     920             :         } else {
     921             :             // -- Legacy System (!TODO: remove after enforcement) --
     922         228 :             CMasternode* pmn = mnodeman.Find(it->first);
     923         344 :             (*it).second.SetValid(pmn && pmn->IsEnabled());
     924             :         }
     925         257 :         ++it;
     926             :     }
     927         261 :     LogPrint(BCLog::MNBUDGET, "Cleaned finalized budget votes for [%s (%s)]. After: %d\n",
     928             :             fbud->GetName(), fbud->GetProposalsStr(), fbud->GetVoteCount());
     929          87 : }
     930             : 
     931          38 : CDataStream CBudgetManager::GetProposalVoteSerialized(const uint256& voteHash) const
     932             : {
     933          38 :     LOCK(cs_votes);
     934          38 :     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
     935          38 :     ss.reserve(1000);
     936          38 :     ss << mapSeenProposalVotes.at(voteHash);
     937          76 :     return ss;
     938             : }
     939             : 
     940         163 : CDataStream CBudgetManager::GetProposalSerialized(const uint256& propHash) const
     941             : {
     942         163 :     LOCK(cs_proposals);
     943         326 :     return mapProposals.at(propHash).GetBroadcast();
     944             : }
     945             : 
     946          31 : CDataStream CBudgetManager::GetFinalizedBudgetVoteSerialized(const uint256& voteHash) const
     947             : {
     948          31 :     LOCK(cs_finalizedvotes);
     949          31 :     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
     950          31 :     ss.reserve(1000);
     951          31 :     ss << mapSeenFinalizedBudgetVotes.at(voteHash);
     952          62 :     return ss;
     953             : }
     954             : 
     955          13 : CDataStream CBudgetManager::GetFinalizedBudgetSerialized(const uint256& budgetHash) const
     956             : {
     957          13 :     LOCK(cs_budgets);
     958          26 :     return mapFinalizedBudgets.at(budgetHash).GetBroadcast();
     959             : }
     960             : 
     961           0 : bool CBudgetManager::AddAndRelayProposalVote(const CBudgetVote& vote, std::string& strError)
     962             : {
     963           0 :     if (UpdateProposal(vote, nullptr, strError)) {
     964           0 :         AddSeenProposalVote(vote);
     965           0 :         vote.Relay();
     966           0 :         return true;
     967             :     }
     968             :     return false;
     969             : }
     970             : 
     971       38613 : void CBudgetManager::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
     972             : {
     973       52291 :     if (g_tiertwo_sync_state.GetSyncPhase() <= MASTERNODE_SYNC_BUDGET) return;
     974             : 
     975       14789 :     if (strBudgetMode == "suggest") { //suggest the budget we see
     976           0 :         SubmitFinalBudget();
     977             :     }
     978             : 
     979       14789 :     int nCurrentHeight = GetBestHeight();
     980             :     //this function should be called 1/14 blocks, allowing up to 100 votes per day on all proposals
     981       14789 :     if (nCurrentHeight % 14 != 0) return;
     982             : 
     983             :     // incremental sync with our peers
     984        1111 :     if (g_tiertwo_sync_state.IsSynced()) {
     985        1111 :         LogPrint(BCLog::MNBUDGET,"%s:  incremental sync started\n", __func__);
     986             :         // Once every 7 days, try to relay the complete budget data
     987        1111 :         if (GetRandInt(Params().IsRegTestNet() ? 2 : 720) == 0) {
     988         539 :             ResetSync();
     989             :         }
     990             : 
     991        1111 :         CBudgetManager* manager = this;
     992        1111 :         g_connman->ForEachNode([manager](CNode* pnode){
     993       11011 :             if (pnode->nVersion >= ActiveProtocol())
     994       11011 :                 manager->Sync(pnode, true);
     995       11011 :         });
     996        1111 :         MarkSynced();
     997             :     }
     998             : 
     999             :     // remove expired/heavily downvoted budgets
    1000        1111 :     CheckAndRemove();
    1001             : 
    1002        1111 :     {
    1003        1111 :         LOCK(cs_proposals);
    1004        1111 :         LogPrint(BCLog::MNBUDGET,"%s:  mapProposals cleanup - size: %d\n", __func__, mapProposals.size());
    1005        2532 :         for (auto& it: mapProposals) {
    1006        1421 :             RemoveStaleVotesOnProposal(&it.second);
    1007             :         }
    1008             :     }
    1009        1111 :     {
    1010        1111 :         LOCK(cs_budgets);
    1011        1111 :         LogPrint(BCLog::MNBUDGET,"%s:  mapFinalizedBudgets cleanup - size: %d\n", __func__, mapFinalizedBudgets.size());
    1012        1198 :         for (auto& it: mapFinalizedBudgets) {
    1013          87 :             RemoveStaleVotesOnFinalBudget(&it.second);
    1014             :         }
    1015             :     }
    1016             : 
    1017        1111 :     int64_t now = GetTime();
    1018        3333 :     const auto cleanOrphans = [now](auto& mutex, auto& mapOrphans, auto& mapSeen) {
    1019        2222 :         LOCK(mutex);
    1020        2222 :         for (auto it = mapOrphans.begin() ; it != mapOrphans.end();) {
    1021           0 :             int64_t lastReceivedVoteTime = it->second.second;
    1022           0 :             if (lastReceivedVoteTime + BUDGET_ORPHAN_VOTES_CLEANUP_SECONDS < now) {
    1023             :                 // Clean seen votes
    1024           0 :                 for (const auto& voteIt : it->second.first) {
    1025           0 :                     mapSeen.erase(voteIt.GetHash());
    1026             :                 }
    1027             :                 // Remove proposal orphan votes
    1028           0 :                 it = mapOrphans.erase(it);
    1029             :             } else {
    1030        2222 :                 it++;
    1031             :             }
    1032             :         }
    1033        3333 :     };
    1034             : 
    1035             :     // Clean orphan proposal votes if no parent arrived after an hour.
    1036        1111 :     cleanOrphans(cs_votes, mapOrphanProposalVotes, mapSeenProposalVotes);
    1037             :     // Clean orphan budget votes if no parent arrived after an hour.
    1038        1111 :     cleanOrphans(cs_finalizedvotes, mapOrphanFinalizedBudgetVotes, mapSeenFinalizedBudgetVotes);
    1039             : 
    1040             :     // Once every 2 weeks (1/14 * 1/1440), clean the seen maps
    1041        1111 :     if (g_tiertwo_sync_state.IsSynced() && GetRandInt(1440) == 0) {
    1042           1 :         ReloadMapSeen();
    1043             :     }
    1044             : 
    1045        1111 :     LogPrint(BCLog::MNBUDGET,"%s:  PASSED\n", __func__);
    1046             : }
    1047             : 
    1048         731 : int CBudgetManager::ProcessBudgetVoteSync(const uint256& nProp, CNode* pfrom)
    1049             : {
    1050        1462 :     if (nProp.IsNull()) {
    1051        2184 :         LOCK2(cs_budgets, cs_proposals);
    1052         728 :         if (!(pfrom->addr.IsRFC1918() || pfrom->addr.IsLocal())) {
    1053           0 :             if (g_netfulfilledman.HasFulfilledRequest(pfrom->addr, BUDGET_SYNC_REQUEST_RECV)) {
    1054           0 :                 LogPrint(BCLog::MASTERNODE, "budgetsync - peer %i already asked for budget sync\n", pfrom->GetId());
    1055             :                 // let's not be so hard with the node for now.
    1056           0 :                 return 10;
    1057             :             }
    1058             :         }
    1059             :     }
    1060             : 
    1061        1462 :     if (nProp.IsNull()) Sync(pfrom, false /* fPartial */);
    1062           3 :     else SyncSingleItem(pfrom, nProp);
    1063         731 :     LogPrint(BCLog::MNBUDGET, "mnvs - Sent Masternode votes to peer %i\n", pfrom->GetId());
    1064             :     return 0;
    1065             : }
    1066             : 
    1067         165 : int CBudgetManager::ProcessProposal(CBudgetProposal& proposal)
    1068             : {
    1069         165 :     const uint256& nHash = proposal.GetHash();
    1070         165 :     if (HaveProposal(nHash)) {
    1071           0 :         g_tiertwo_sync_state.AddedBudgetItem(nHash);
    1072           0 :         return 0;
    1073             :     }
    1074         165 :     if (!AddProposal(proposal)) {
    1075             :         return 0;
    1076             :     }
    1077             : 
    1078             :     // Relay only if we are synchronized
    1079             :     // Makes no sense to relay proposals to the peers from where we are syncing them.
    1080         165 :     if (g_tiertwo_sync_state.IsSynced()) proposal.Relay();
    1081         165 :     g_tiertwo_sync_state.AddedBudgetItem(nHash);
    1082             : 
    1083         330 :     LogPrint(BCLog::MNBUDGET, "mprop (new) %s\n", nHash.ToString());
    1084             :     //We might have active votes for this proposal that are valid now
    1085         165 :     CheckOrphanVotes();
    1086             :     return 0;
    1087             : }
    1088             : 
    1089          45 : bool CBudgetManager::ProcessProposalVote(CBudgetVote& vote, CNode* pfrom, CValidationState& state)
    1090             : {
    1091          45 :     const uint256& voteID = vote.GetHash();
    1092             : 
    1093          45 :     if (HaveSeenProposalVote(voteID)) {
    1094           0 :         g_tiertwo_sync_state.AddedBudgetItem(voteID);
    1095           0 :         return false;
    1096             :     }
    1097             : 
    1098          90 :     std::string err;
    1099          45 :     if (vote.GetTime() > GetTime() + (60 * 60)) {
    1100           0 :         err = strprintf("new vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n",
    1101           0 :                              vote.GetHash().ToString(), vote.GetTime(), GetTime() + (60 * 60));
    1102           0 :         return state.Invalid(false, REJECT_INVALID, "bad-mvote", err);
    1103             :     }
    1104             : 
    1105          90 :     const CTxIn& voteVin = vote.GetVin();
    1106             : 
    1107             :     // See if this vote was signed with a deterministic masternode
    1108          90 :     auto mnList = deterministicMNManager->GetListAtChainTip();
    1109          90 :     auto dmn = mnList.GetMNByCollateral(voteVin.prevout);
    1110          45 :     if (dmn) {
    1111          20 :         const std::string& mn_protx_id = dmn->proTxHash.ToString();
    1112             : 
    1113          10 :         if (dmn->IsPoSeBanned()) {
    1114           0 :             err = strprintf("masternode (%s) not valid or PoSe banned", mn_protx_id);
    1115           0 :             return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, err);
    1116             :         }
    1117             : 
    1118          10 :         AddSeenProposalVote(vote);
    1119             : 
    1120          10 :         if (!vote.CheckSignature(dmn->pdmnState->keyIDVoting)) {
    1121           0 :             err = strprintf("invalid mvote sig from dmn: %s", mn_protx_id);
    1122           0 :             return state.DoS(100, false, REJECT_INVALID, "bad-mvote-sig", false, err);
    1123             :         }
    1124             : 
    1125          10 :         if (!UpdateProposal(vote, pfrom, err)) {
    1126           0 :             return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, strprintf("%s (%s)", err, mn_protx_id));
    1127             :         }
    1128             : 
    1129             :         // Relay only if we are synchronized
    1130             :         // Makes no sense to relay votes to the peers from where we are syncing them.
    1131          10 :         if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
    1132          10 :         g_tiertwo_sync_state.AddedBudgetItem(voteID);
    1133          30 :         LogPrint(BCLog::MNBUDGET, "mvote - new vote (%s) for proposal %s from dmn %s\n",
    1134             :                 voteID.ToString(), vote.GetProposalHash().ToString(), mn_protx_id);
    1135          10 :         return true;
    1136             :     }
    1137             : 
    1138             :     // -- Legacy System (!TODO: remove after enforcement) --
    1139             : 
    1140          35 :     CMasternode* pmn = mnodeman.Find(voteVin.prevout);
    1141          35 :     if (!pmn) {
    1142           0 :         err = strprintf("unknown masternode - vin: %s", voteVin.prevout.ToString());
    1143             :         // Ask for MN only if we finished syncing the MN list.
    1144           0 :         if (pfrom && g_tiertwo_sync_state.IsMasternodeListSynced()) mnodeman.AskForMN(pfrom, voteVin);
    1145           0 :         return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, err);
    1146             :     }
    1147             : 
    1148          35 :     if (!pmn->IsEnabled()) {
    1149           0 :         return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, "masternode not valid");
    1150             :     }
    1151             : 
    1152          35 :     AddSeenProposalVote(vote);
    1153             : 
    1154          35 :     if (!vote.CheckSignature(pmn->pubKeyMasternode.GetID())) {
    1155           0 :         if (g_tiertwo_sync_state.IsSynced()) {
    1156           0 :             err = strprintf("signature from masternode %s invalid", voteVin.prevout.ToString());
    1157           0 :             return state.DoS(20, false, REJECT_INVALID, "bad-mvote-sig", false, err);
    1158             :         }
    1159             :         return false;
    1160             :     }
    1161             : 
    1162          35 :     if (!UpdateProposal(vote, pfrom, err)) {
    1163           2 :         return state.DoS(0, false, REJECT_INVALID, "bad-mvote", false, err);
    1164             :     }
    1165             : 
    1166             :     // Relay only if we are synchronized
    1167             :     // Makes no sense to relay votes to the peers from where we are syncing them.
    1168          34 :     if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
    1169          34 :     g_tiertwo_sync_state.AddedBudgetItem(voteID);
    1170         136 :     LogPrint(BCLog::MNBUDGET, "mvote - new vote (%s) for proposal %s from dmn %s\n",
    1171             :             voteID.ToString(), vote.GetProposalHash().ToString(), voteVin.prevout.ToString());
    1172             :     return true;
    1173             : }
    1174             : 
    1175          14 : int CBudgetManager::ProcessFinalizedBudget(CFinalizedBudget& finalbudget, CNode* pfrom)
    1176             : {
    1177             : 
    1178          14 :     const uint256& nHash = finalbudget.GetHash();
    1179          14 :     if (HaveFinalizedBudget(nHash)) {
    1180           0 :         g_tiertwo_sync_state.AddedBudgetItem(nHash);
    1181           0 :         return 0;
    1182             :     }
    1183          14 :     if (!AddFinalizedBudget(finalbudget, pfrom)) {
    1184             :         return 0;
    1185             :     }
    1186             : 
    1187             :     // Relay only if we are synchronized
    1188             :     // Makes no sense to relay finalizations to the peers from where we are syncing them.
    1189          12 :     if (g_tiertwo_sync_state.IsSynced()) finalbudget.Relay();
    1190          12 :     g_tiertwo_sync_state.AddedBudgetItem(nHash);
    1191             : 
    1192          24 :     LogPrint(BCLog::MNBUDGET, "fbs (new) %s\n", nHash.ToString());
    1193             :     //we might have active votes for this budget that are now valid
    1194          12 :     CheckOrphanVotes();
    1195             :     return 0;
    1196             : }
    1197             : 
    1198          36 : bool CBudgetManager::ProcessFinalizedBudgetVote(CFinalizedBudgetVote& vote, CNode* pfrom, CValidationState& state)
    1199             : {
    1200          36 :     const uint256& voteID = vote.GetHash();
    1201             : 
    1202          36 :     if (HaveSeenFinalizedBudgetVote(voteID)) {
    1203           0 :         g_tiertwo_sync_state.AddedBudgetItem(voteID);
    1204           0 :         return false;
    1205             :     }
    1206             : 
    1207          72 :     std::string err;
    1208          36 :     if (vote.GetTime() > GetTime() + (60 * 60)) {
    1209           0 :         err = strprintf("new vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n",
    1210           0 :                              vote.GetHash().ToString(), vote.GetTime(), GetTime() + (60 * 60));
    1211           0 :         return state.Invalid(false, REJECT_INVALID, "bad-fbvote", err);
    1212             :     }
    1213             : 
    1214          72 :     const CTxIn& voteVin = vote.GetVin();
    1215             : 
    1216             :     // See if this vote was signed with a deterministic masternode
    1217          72 :     auto mnList = deterministicMNManager->GetListAtChainTip();
    1218          72 :     auto dmn = mnList.GetMNByCollateral(voteVin.prevout);
    1219          36 :     if (dmn) {
    1220          18 :         const std::string& mn_protx_id = dmn->proTxHash.ToString();
    1221             : 
    1222           9 :         if (dmn->IsPoSeBanned()) {
    1223           0 :             err = strprintf("masternode (%s) not valid or PoSe banned", mn_protx_id);
    1224           0 :             return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, err);
    1225             :         }
    1226             : 
    1227           9 :         AddSeenFinalizedBudgetVote(vote);
    1228             : 
    1229           9 :         if (!vote.CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) {
    1230           0 :             err = strprintf("invalid fbvote sig from dmn: %s", mn_protx_id);
    1231           0 :             return state.DoS(100, false, REJECT_INVALID, "bad-fbvote-sig", false, err);
    1232             :         }
    1233             : 
    1234           9 :         if (!UpdateFinalizedBudget(vote, pfrom, err)) {
    1235           3 :             return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, strprintf("%s (%s)", err, mn_protx_id));
    1236             :         }
    1237             : 
    1238             :         // Relay only if we are synchronized
    1239             :         // Makes no sense to relay votes to the peers from where we are syncing them.
    1240           8 :         if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
    1241           8 :         g_tiertwo_sync_state.AddedBudgetItem(voteID);
    1242          24 :         LogPrint(BCLog::MNBUDGET, "fbvote - new vote (%s) for budget %s from dmn %s\n",
    1243             :                 voteID.ToString(), vote.GetBudgetHash().ToString(), mn_protx_id);
    1244           8 :         return true;
    1245             :     }
    1246             : 
    1247             :     // -- Legacy System (!TODO: remove after enforcement) --
    1248          27 :     CMasternode* pmn = mnodeman.Find(voteVin.prevout);
    1249          27 :     if (!pmn) {
    1250           0 :         err = strprintf("unknown masternode - vin: %s", voteVin.prevout.ToString());
    1251             :         // Ask for MN only if we finished syncing the MN list.
    1252           0 :         if (pfrom && g_tiertwo_sync_state.IsMasternodeListSynced()) mnodeman.AskForMN(pfrom, voteVin);
    1253           0 :         return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, err);
    1254             :     }
    1255             : 
    1256          27 :     if (!pmn->IsEnabled()) {
    1257           0 :         return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, "masternode not valid");
    1258             :     }
    1259             : 
    1260          27 :     AddSeenFinalizedBudgetVote(vote);
    1261             : 
    1262          27 :     if (!vote.CheckSignature(pmn->pubKeyMasternode.GetID())) {
    1263           0 :         if (g_tiertwo_sync_state.IsSynced()) {
    1264           0 :             err = strprintf("signature from masternode %s invalid", voteVin.prevout.ToString());
    1265           0 :             return state.DoS(20, false, REJECT_INVALID, "bad-fbvote-sig", false, err);
    1266             :         }
    1267             :         return false;
    1268             :     }
    1269             : 
    1270          27 :     if (!UpdateFinalizedBudget(vote, pfrom, err)) {
    1271           4 :         return state.DoS(0, false, REJECT_INVALID, "bad-fbvote", false, err);
    1272             :     }
    1273             : 
    1274             :     // Relay only if we are synchronized
    1275             :     // Makes no sense to relay votes to the peers from where we are syncing them.
    1276          25 :     if (g_tiertwo_sync_state.IsSynced()) vote.Relay();
    1277          25 :     g_tiertwo_sync_state.AddedBudgetItem(voteID);
    1278         100 :     LogPrint(BCLog::MNBUDGET, "fbvote - new vote (%s) for budget %s from mn %s\n",
    1279             :             voteID.ToString(), vote.GetBudgetHash().ToString(), voteVin.prevout.ToString());
    1280             :     return true;
    1281             : }
    1282             : 
    1283       55744 : bool CBudgetManager::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv, int& banScore)
    1284             : {
    1285       55744 :     banScore = ProcessMessageInner(pfrom, strCommand, vRecv);
    1286       55744 :     return banScore == 0;
    1287             : }
    1288             : 
    1289       55744 : int CBudgetManager::ProcessMessageInner(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
    1290             : {
    1291       55744 :     if (!g_tiertwo_sync_state.IsBlockchainSynced()) return 0;
    1292             : 
    1293       55734 :     if (strCommand == NetMsgType::BUDGETVOTESYNC) {
    1294             :         // Masternode vote sync
    1295         731 :         uint256 nProp;
    1296         731 :         vRecv >> nProp;
    1297         731 :         return ProcessBudgetVoteSync(nProp, pfrom);
    1298             :     }
    1299             : 
    1300       55003 :     if (strCommand == NetMsgType::BUDGETPROPOSAL) {
    1301             :         // Masternode Proposal
    1302         330 :         CBudgetProposal proposal;
    1303         165 :         if (!proposal.ParseBroadcast(vRecv)) {
    1304             :             return 20;
    1305             :         }
    1306         165 :         {
    1307             :             // Clear inv request
    1308         165 :             LOCK(cs_main);
    1309         165 :             g_connman->RemoveAskFor(proposal.GetHash(), MSG_BUDGET_PROPOSAL);
    1310             :         }
    1311         165 :         return ProcessProposal(proposal);
    1312             :     }
    1313             : 
    1314       54838 :     if (strCommand == NetMsgType::BUDGETVOTE) {
    1315          76 :         CBudgetVote vote;
    1316          38 :         vRecv >> vote;
    1317          38 :         vote.SetValid(true);
    1318             : 
    1319          38 :         {
    1320             :             // Clear inv request
    1321          38 :             LOCK(cs_main);
    1322          38 :             g_connman->RemoveAskFor(vote.GetHash(), MSG_BUDGET_VOTE);
    1323             :         }
    1324             : 
    1325          76 :         CValidationState state;
    1326          38 :         if (!ProcessProposalVote(vote, pfrom, state)) {
    1327           1 :             int nDos = 0;
    1328           1 :             if (state.IsInvalid(nDos)) {
    1329           2 :                 LogPrint(BCLog::MNBUDGET, "%s: %s\n", __func__, FormatStateMessage(state));
    1330             :             }
    1331           1 :             return nDos;
    1332             :         }
    1333             :         return 0;
    1334             :     }
    1335             : 
    1336       54800 :     if (strCommand == NetMsgType::FINALBUDGET) {
    1337             :         // Finalized Budget Suggestion
    1338          28 :         CFinalizedBudget finalbudget;
    1339          14 :         if (!finalbudget.ParseBroadcast(vRecv)) {
    1340             :             return 20;
    1341             :         }
    1342          14 :         {
    1343             :             // Clear inv request
    1344          14 :             LOCK(cs_main);
    1345          14 :             g_connman->RemoveAskFor(finalbudget.GetHash(), MSG_BUDGET_FINALIZED);
    1346             :         }
    1347          14 :         return ProcessFinalizedBudget(finalbudget, pfrom);
    1348             :     }
    1349             : 
    1350       54786 :     if (strCommand == NetMsgType::FINALBUDGETVOTE) {
    1351          62 :         CFinalizedBudgetVote vote;
    1352          31 :         vRecv >> vote;
    1353          31 :         vote.SetValid(true);
    1354             : 
    1355          31 :         {
    1356             :             // Clear inv request
    1357          31 :             LOCK(cs_main);
    1358          31 :             g_connman->RemoveAskFor(vote.GetHash(), MSG_BUDGET_FINALIZED_VOTE);
    1359             :         }
    1360             : 
    1361          62 :         CValidationState state;
    1362          31 :         if (!ProcessFinalizedBudgetVote(vote, pfrom, state)) {
    1363           3 :             int nDos = 0;
    1364           3 :             if (state.IsInvalid(nDos)) {
    1365           6 :                 LogPrint(BCLog::MNBUDGET, "%s: %s\n", __func__, FormatStateMessage(state));
    1366             :             }
    1367           3 :             return nDos;
    1368             :         }
    1369             :         return 0;
    1370             :     }
    1371             : 
    1372             :     // nothing was done
    1373             :     return 0;
    1374             : }
    1375             : 
    1376        2005 : void CBudgetManager::SetSynced(bool synced)
    1377             : {
    1378        2005 :     {
    1379        2005 :         LOCK(cs_proposals);
    1380        4102 :         for (auto& it: mapProposals) {
    1381        2097 :             CBudgetProposal* pbudgetProposal = &(it.second);
    1382        2097 :             if (pbudgetProposal && pbudgetProposal->IsValid()) {
    1383             :                 //mark votes
    1384        2097 :                 pbudgetProposal->SetSynced(synced);
    1385             :             }
    1386             :         }
    1387             :     }
    1388        2005 :     {
    1389        2005 :         LOCK(cs_budgets);
    1390        2134 :         for (auto& it: mapFinalizedBudgets) {
    1391         129 :             CFinalizedBudget* pfinalizedBudget = &(it.second);
    1392         129 :             if (pfinalizedBudget && pfinalizedBudget->IsValid()) {
    1393             :                 //mark votes
    1394         129 :                 pfinalizedBudget->SetSynced(synced);
    1395             :             }
    1396             :         }
    1397             :     }
    1398        2005 : }
    1399             : 
    1400             : template<typename T>
    1401           4 : static bool relayItemIfFound(const uint256& itemHash, CNode* pfrom, RecursiveMutex& cs, std::map<uint256, T>& map, const char* type)
    1402             : {
    1403           4 :     CNetMsgMaker msgMaker(pfrom->GetSendVersion());
    1404           8 :     LOCK(cs);
    1405           4 :     const auto& it = map.find(itemHash);
    1406           4 :     if (it == map.end()) return false;
    1407           3 :     T* item = &(it->second);
    1408           3 :     if (!item->IsValid()) return true; // don't broadcast invalid items
    1409           3 :     g_connman->PushMessage(pfrom, msgMaker.Make(type, item->GetBroadcast()));
    1410           3 :     int nInvCount = 1;
    1411           3 :     item->SyncVotes(pfrom, false /* fPartial */, nInvCount);
    1412           3 :     LogPrint(BCLog::MNBUDGET, "%s: single %s sent %d items\n", __func__, type, nInvCount);
    1413             :     return true;
    1414             : }
    1415             : 
    1416             : template<typename T>
    1417       23478 : static void relayInventoryItems(CNode* pfrom, RecursiveMutex& cs, std::map<uint256, T>& map, bool fPartial, GetDataMsg invType, const int mn_sync_budget_type)
    1418             : {
    1419       23478 :     CNetMsgMaker msgMaker(pfrom->GetSendVersion());
    1420       23478 :     int nInvCount = 0;
    1421             :     {
    1422       46956 :         LOCK(cs);
    1423       28949 :         for (auto& it: map) {
    1424        5471 :             T* item = &(it.second);
    1425        5471 :             if (item && item->IsValid()) {
    1426        5471 :                 pfrom->PushInventory(CInv(invType, item->GetHash()));
    1427        5471 :                 nInvCount++;
    1428        5471 :                 item->SyncVotes(pfrom, fPartial, nInvCount);
    1429             :             }
    1430             :         }
    1431             :     }
    1432       23478 :     g_connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, mn_sync_budget_type, nInvCount));
    1433       23478 :     LogPrint(BCLog::MNBUDGET, "%s: sent %d items\n", __func__, nInvCount);
    1434       23478 : }
    1435             : 
    1436           3 : void CBudgetManager::SyncSingleItem(CNode* pfrom, const uint256& nProp)
    1437             : {
    1438           6 :     if (nProp.IsNull()) return;
    1439             :     // Try first to relay a proposal
    1440           3 :     if (relayItemIfFound<CBudgetProposal>(nProp, pfrom, cs_proposals, mapProposals, NetMsgType::BUDGETPROPOSAL)) {
    1441             :         return;
    1442             :     }
    1443             :     // Try now to relay a finalization
    1444           1 :     if (relayItemIfFound<CFinalizedBudget>(nProp, pfrom, cs_budgets, mapFinalizedBudgets, NetMsgType::FINALBUDGET)) {
    1445             :         return;
    1446             :     }
    1447           0 :     LogPrint(BCLog::MNBUDGET, "%s: single request budget item not found\n", __func__);
    1448             : }
    1449             : 
    1450             : 
    1451       11739 : void CBudgetManager::Sync(CNode* pfrom, bool fPartial)
    1452             : {
    1453             :     // Full budget sync request.
    1454       11739 :     relayInventoryItems<CBudgetProposal>(pfrom, cs_proposals, mapProposals, fPartial, MSG_BUDGET_PROPOSAL, MASTERNODE_SYNC_BUDGET_PROP);
    1455       11739 :     relayInventoryItems<CFinalizedBudget>(pfrom, cs_budgets, mapFinalizedBudgets, fPartial, MSG_BUDGET_FINALIZED, MASTERNODE_SYNC_BUDGET_FIN);
    1456             : 
    1457       11739 :     if (!fPartial) {
    1458             :         // We are not going to answer full budget sync requests for an hour (chainparams.FulfilledRequestExpireTime()).
    1459             :         // The remote peer can still do single prop and mnv sync requests if needed.
    1460         728 :         g_netfulfilledman.AddFulfilledRequest(pfrom->addr, BUDGET_SYNC_REQUEST_RECV);
    1461             :     }
    1462       11739 : }
    1463             : 
    1464             : template<typename T>
    1465           4 : static void TryAppendOrphanVoteMap(const T& vote,
    1466             :                                    const uint256& parentHash,
    1467             :                                    std::map<uint256, std::pair<std::vector<T>, int64_t>>& mapOrphan,
    1468             :                                    std::map<uint256, T>& mapSeen)
    1469             : {
    1470           4 :     if (mapOrphan.size() > ORPHAN_VOTES_CACHE_LIMIT) {
    1471             :         // future: notify user about this
    1472           0 :         mapSeen.erase(vote.GetHash());
    1473             :     } else {
    1474             :         // Append orphan vote
    1475           4 :         const auto& it = mapOrphan.find(parentHash);
    1476           4 :         if (it != mapOrphan.end()) {
    1477             :             // Check size limit and erase it from the seen map if we already passed it
    1478           2 :             if (it->second.first.size() > ORPHAN_VOTES_CACHE_LIMIT) {
    1479             :                 // future: check if the MN already voted and replace vote
    1480           0 :                 mapSeen.erase(vote.GetHash());
    1481             :             } else {
    1482           2 :                 it->second.first.emplace_back(vote);
    1483           2 :                 it->second.second = GetTime();
    1484             :             }
    1485             :         } else {
    1486           4 :             mapOrphan.emplace(parentHash, std::make_pair<std::vector<T>, int64_t>({vote}, GetTime()));
    1487             :         }
    1488             :     }
    1489           4 : }
    1490             : 
    1491          45 : bool CBudgetManager::UpdateProposal(const CBudgetVote& vote, CNode* pfrom, std::string& strError)
    1492             : {
    1493          90 :     LOCK(cs_proposals);
    1494             : 
    1495          45 :     const uint256& nProposalHash = vote.GetProposalHash();
    1496          45 :     const auto& itProposal = mapProposals.find(nProposalHash);
    1497          45 :     if (itProposal == mapProposals.end()) {
    1498           1 :         if (pfrom) {
    1499             :             // only ask for missing items after our syncing process is complete --
    1500             :             //   otherwise we'll think a full sync succeeded when they return a result
    1501           1 :             if (!g_tiertwo_sync_state.IsSynced()) return false;
    1502             : 
    1503           2 :             LogPrint(BCLog::MNBUDGET,"%s: Unknown proposal %d, asking for source proposal\n", __func__, nProposalHash.ToString());
    1504           1 :             {
    1505           1 :                 LOCK(cs_votes);
    1506           1 :                 TryAppendOrphanVoteMap<CBudgetVote>(vote, nProposalHash, mapOrphanProposalVotes, mapSeenProposalVotes);
    1507             :             }
    1508             : 
    1509           1 :             if (!g_netfulfilledman.HasItemRequest(pfrom->addr, nProposalHash)) {
    1510           1 :                 g_connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::BUDGETVOTESYNC, nProposalHash));
    1511           1 :                 g_netfulfilledman.AddItemRequest(pfrom->addr, nProposalHash);
    1512             :             }
    1513             :         }
    1514             : 
    1515          45 :         strError = "Proposal not found!";
    1516             :         return false;
    1517             :     }
    1518             : 
    1519             :     // Add or update vote
    1520          44 :     return itProposal->second.AddOrUpdateVote(vote, strError);
    1521             : }
    1522             : 
    1523          37 : bool CBudgetManager::UpdateFinalizedBudget(const CFinalizedBudgetVote& vote, CNode* pfrom, std::string& strError)
    1524             : {
    1525          74 :     LOCK(cs_budgets);
    1526             : 
    1527          37 :     const uint256& nBudgetHash = vote.GetBudgetHash();
    1528          37 :     if (!mapFinalizedBudgets.count(nBudgetHash)) {
    1529           3 :         if (pfrom) {
    1530             :             // only ask for missing items after our syncing process is complete --
    1531             :             //   otherwise we'll think a full sync succeeded when they return a result
    1532           3 :             if (!g_tiertwo_sync_state.IsSynced()) return false;
    1533             : 
    1534           6 :             LogPrint(BCLog::MNBUDGET,"%s: Unknown Finalized Proposal %s, asking for source budget\n", __func__, nBudgetHash.ToString());
    1535           3 :             {
    1536           3 :                 LOCK(cs_finalizedvotes);
    1537           3 :                 TryAppendOrphanVoteMap<CFinalizedBudgetVote>(vote, nBudgetHash, mapOrphanFinalizedBudgetVotes, mapSeenFinalizedBudgetVotes);
    1538             :             }
    1539             : 
    1540           3 :             if (!g_netfulfilledman.HasItemRequest(pfrom->addr, nBudgetHash)) {
    1541           1 :                 g_connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::BUDGETVOTESYNC, nBudgetHash));
    1542           1 :                 g_netfulfilledman.AddItemRequest(pfrom->addr, nBudgetHash);
    1543             :             }
    1544             :         }
    1545             : 
    1546           3 :         strError = "Finalized Budget " + nBudgetHash.ToString() +  " not found!";
    1547           3 :         return false;
    1548             :     }
    1549          68 :     LogPrint(BCLog::MNBUDGET,"%s: Finalized Proposal %s added\n", __func__, nBudgetHash.ToString());
    1550          34 :     return mapFinalizedBudgets[nBudgetHash].AddOrUpdateVote(vote, strError);
    1551             : }
    1552             : 
    1553         136 : std::string CBudgetManager::ToString() const
    1554             : {
    1555         272 :     unsigned int nProposals = WITH_LOCK(cs_proposals, return mapProposals.size(); );
    1556         272 :     unsigned int nBudgets = WITH_LOCK(cs_budgets, return mapFinalizedBudgets.size(); );
    1557             : 
    1558         136 :     unsigned int nSeenVotes = 0, nOrphanVotes = 0;
    1559         136 :     {
    1560         136 :         LOCK(cs_votes);
    1561         136 :         nSeenVotes = mapSeenProposalVotes.size();
    1562         136 :         nOrphanVotes = mapOrphanProposalVotes.size();
    1563             :     }
    1564             : 
    1565         136 :     unsigned int nSeenFinalizedVotes = 0, nOrphanFinalizedVotes = 0;
    1566         136 :     {
    1567         136 :         LOCK(cs_finalizedvotes);
    1568         136 :         nSeenFinalizedVotes = mapSeenFinalizedBudgetVotes.size();
    1569         136 :         nOrphanFinalizedVotes = mapOrphanFinalizedBudgetVotes.size();
    1570             :     }
    1571             : 
    1572         136 :     return strprintf("Proposals: %d - Finalized Budgets: %d - "
    1573             :             "Proposal Votes: %d (orphan: %d) - "
    1574             :             "Finalized Budget Votes: %d (orphan: %d)",
    1575             :             nProposals, nBudgets,
    1576         136 :             nSeenVotes, nOrphanVotes, nSeenFinalizedVotes, nOrphanFinalizedVotes);
    1577             : }
    1578             : 
    1579             : 
    1580             : /*
    1581             :  * Check Collateral
    1582             :  */
    1583         201 : bool CheckCollateralConfs(const uint256& nTxCollateralHash, int nCurrentHeight, int nProposalHeight, std::string& strError)
    1584             : {
    1585         201 :     const int nRequiredConfs = Params().GetConsensus().nBudgetFeeConfirmations;
    1586         201 :     const int nConf = nCurrentHeight - nProposalHeight + 1;
    1587             : 
    1588         201 :     if (nConf < nRequiredConfs) {
    1589           0 :         strError = strprintf("Collateral requires at least %d confirmations - %d confirmations (current height: %d, fee tx height: %d)",
    1590           0 :                 nRequiredConfs, nConf, nCurrentHeight, nProposalHeight);
    1591           0 :         LogPrint(BCLog::MNBUDGET,"%s: %s\n", __func__, strError);
    1592           0 :         return false;
    1593             :     }
    1594             :     return true;
    1595             : }
    1596             : 
    1597         201 : bool CheckCollateral(const uint256& nTxCollateralHash, const uint256& nExpectedHash, std::string& strError, int64_t& nTime, int nCurrentHeight, bool fBudgetFinalization)
    1598             : {
    1599         201 :     CTransactionRef txCollateral;
    1600         201 :     uint256 nBlockHash;
    1601         201 :     if (!GetTransaction(nTxCollateralHash, txCollateral, nBlockHash, true)) {
    1602           0 :         strError = strprintf("Can't find collateral tx %s", nTxCollateralHash.ToString());
    1603           0 :         return false;
    1604             :     }
    1605             : 
    1606         201 :     if (txCollateral->vout.size() < 1) return false;
    1607         201 :     if (txCollateral->nLockTime != 0) return false;
    1608             : 
    1609         402 :     CScript findScript;
    1610         201 :     findScript << OP_RETURN << ToByteVector(nExpectedHash);
    1611             : 
    1612         201 :     bool foundOpReturn = false;
    1613         335 :     for (const CTxOut &o : txCollateral->vout) {
    1614         335 :         if (!o.scriptPubKey.IsPayToPublicKeyHash() && !o.scriptPubKey.IsUnspendable()) {
    1615           0 :             strError = strprintf("Invalid Script %s", txCollateral->ToString());
    1616           0 :             return false;
    1617             :         }
    1618         335 :         if (fBudgetFinalization) {
    1619             :             // Collateral for budget finalization
    1620             :             // Note: there are still old valid budgets out there, but the check for the new 5 PIV finalization collateral
    1621             :             //       will also cover the old 50 PIV finalization collateral.
    1622         150 :             LogPrint(BCLog::MNBUDGET, "Final Budget: o.scriptPubKey(%s) == findScript(%s) ?\n", HexStr(o.scriptPubKey), HexStr(findScript));
    1623          30 :             if (o.scriptPubKey == findScript) {
    1624          17 :                 LogPrint(BCLog::MNBUDGET, "Final Budget: o.nValue(%ld) >= BUDGET_FEE_TX(%ld) ?\n", o.nValue, BUDGET_FEE_TX);
    1625          17 :                 if(o.nValue >= BUDGET_FEE_TX) {
    1626             :                     foundOpReturn = true;
    1627             :                     break;
    1628             :                 }
    1629             :             }
    1630             :         } else {
    1631             :             // Collateral for normal budget proposal
    1632        1525 :             LogPrint(BCLog::MNBUDGET, "Normal Budget: o.scriptPubKey(%s) == findScript(%s) ?\n", HexStr(o.scriptPubKey), HexStr(findScript));
    1633         305 :             if (o.scriptPubKey == findScript) {
    1634         184 :                 LogPrint(BCLog::MNBUDGET, "Normal Budget: o.nValue(%ld) >= PROPOSAL_FEE_TX(%ld) ?\n", o.nValue, PROPOSAL_FEE_TX);
    1635         184 :                 if(o.nValue >= PROPOSAL_FEE_TX) {
    1636             :                     foundOpReturn = true;
    1637             :                     break;
    1638             :                 }
    1639             :             }
    1640             :         }
    1641             :     }
    1642             : 
    1643         201 :     if (!foundOpReturn) {
    1644           0 :         strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral->ToString());
    1645           0 :         return false;
    1646             :     }
    1647             : 
    1648             :     // Retrieve block height (checking that it's in the active chain) and time
    1649             :     // both get set in CBudgetProposal/CFinalizedBudget by the caller (AddProposal/AddFinalizedBudget)
    1650         402 :     if (nBlockHash.IsNull()) {
    1651           0 :         strError = strprintf("Collateral transaction %s is unconfirmed", nTxCollateralHash.ToString());
    1652           0 :         return false;
    1653             :     }
    1654         201 :     nTime = 0;
    1655         201 :     int nProposalHeight = 0;
    1656         201 :     {
    1657         201 :         LOCK(cs_main);
    1658         201 :         CBlockIndex* pindex = LookupBlockIndex(nBlockHash);
    1659         402 :         if (pindex && chainActive.Contains(pindex)) {
    1660         201 :             nProposalHeight = pindex->nHeight;
    1661         201 :             nTime = pindex->nTime;
    1662             :         }
    1663             :     }
    1664             : 
    1665         201 :     if (!nProposalHeight) {
    1666           0 :         strError = strprintf("Collateral transaction %s not in Active chain", nTxCollateralHash.ToString());
    1667           0 :         return false;
    1668             :     }
    1669             : 
    1670         201 :     return CheckCollateralConfs(nTxCollateralHash, nCurrentHeight, nProposalHeight, strError);
    1671             : }

Generated by: LCOV version 1.14