LCOV - code coverage report
Current view: top level - src/governance - superblock.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 84 387 21.7 %
Date: 2026-06-25 07:23:51 Functions: 12 34 35.3 %

          Line data    Source code
       1             : // Copyright (c) 2014-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <governance/superblock.h>
       6             : 
       7             : #include <chainparams.h>
       8             : #include <core_io.h>
       9             : #include <evo/deterministicmns.h>
      10             : #include <governance/object.h>
      11             : #include <governance/vote.h>
      12             : #include <key_io.h>
      13             : #include <logging.h>
      14             : #include <primitives/transaction.h>
      15             : #include <util/std23.h>
      16             : #include <util/strencodings.h>
      17             : #include <util/time.h>
      18             : #include <validation.h>
      19             : 
      20             : #include <univalue.h>
      21             : 
      22           0 : CAmount ParsePaymentAmount(const std::string& strAmount)
      23             : {
      24           0 :     CAmount nAmount = 0;
      25           0 :     if (strAmount.empty()) {
      26           0 :         throw std::runtime_error(strprintf("%s -- Amount is empty", __func__));
      27             :     }
      28           0 :     if (strAmount.size() > 20) {
      29             :         // String is much too long, the functions below impose stricter
      30             :         // requirements
      31           0 :         throw std::runtime_error(strprintf("%s -- Amount string too long", __func__));
      32             :     }
      33             :     // Make sure the string makes sense as an amount
      34             :     // Note: No spaces allowed
      35             :     // Also note: No scientific notation
      36           0 :     size_t pos = strAmount.find_first_not_of("0123456789.");
      37           0 :     if (pos != std::string::npos) {
      38           0 :         throw std::runtime_error(strprintf("%s -- Amount string contains invalid character", __func__));
      39             :     }
      40             : 
      41           0 :     pos = strAmount.find('.');
      42           0 :     if (pos == 0) {
      43             :         // JSON doesn't allow values to start with a decimal point
      44           0 :         throw std::runtime_error(strprintf("%s -- Invalid amount string, leading decimal point not allowed", __func__));
      45             :     }
      46             : 
      47             :     // Make sure there's no more than 1 decimal point
      48           0 :     if ((pos != std::string::npos) && (strAmount.find('.', pos + 1) != std::string::npos)) {
      49           0 :         throw std::runtime_error(strprintf("%s -- Invalid amount string, too many decimal points", __func__));
      50             :     }
      51             : 
      52             :     // Note this code is taken from AmountFromValue in rpcserver.cpp
      53             :     // which is used for parsing the amounts in createrawtransaction.
      54           0 :     if (!ParseFixedPoint(strAmount, 8, &nAmount)) {
      55           0 :         nAmount = 0;
      56           0 :         throw std::runtime_error(strprintf("%s -- ParseFixedPoint failed for string \"%s\"", __func__, strAmount));
      57             :     }
      58           0 :     if (!MoneyRange(nAmount)) {
      59           0 :         nAmount = 0;
      60           0 :         throw std::runtime_error(strprintf("%s -- Invalid amount string, value outside of valid money range", __func__));
      61             :     }
      62             : 
      63           0 :     return nAmount;
      64           0 : }
      65             : 
      66             : CSuperblock::
      67           0 :     CSuperblock() :
      68           0 :     nGovObjHash(),
      69           0 :     nBlockHeight(0),
      70           0 :     nStatus(SeenObjectStatus::Unknown),
      71           0 :     vecPayments()
      72           0 : {
      73           0 : }
      74             : 
      75           0 : CSuperblock::CSuperblock(const CGovernanceObject& govObj, uint256& nHash) :
      76           0 :     nGovObjHash(nHash),
      77           0 :     nBlockHeight(0),
      78           0 :     nStatus(SeenObjectStatus::Unknown),
      79           0 :     vecPayments()
      80           0 : {
      81           0 :     LogPrint(BCLog::GOBJECT, "CSuperblock -- Constructor govobj: %s, nObjectType = %d\n", govObj.GetDataAsPlainString(),
      82             :              std23::to_underlying(govObj.GetObjectType()));
      83             : 
      84           0 :     if (govObj.GetObjectType() != GovernanceObject::TRIGGER) {
      85           0 :         throw std::runtime_error("CSuperblock: Governance Object not a trigger");
      86             :     }
      87             : 
      88           0 :     UniValue obj = govObj.GetJSONObject();
      89             : 
      90           0 :     if (obj["type"].getInt<int>() != std23::to_underlying(GovernanceObject::TRIGGER)) {
      91           0 :         throw std::runtime_error("CSuperblock: invalid data type");
      92             :     }
      93             : 
      94             :     // FIRST WE GET THE START HEIGHT, THE BLOCK HEIGHT AT WHICH THE PAYMENT SHALL OCCUR
      95           0 :     nBlockHeight = obj["event_block_height"].getInt<int>();
      96             : 
      97             :     // NEXT WE GET THE PAYMENT INFORMATION AND RECONSTRUCT THE PAYMENT VECTOR
      98           0 :     std::string strAddresses = obj["payment_addresses"].get_str();
      99           0 :     std::string strAmounts = obj["payment_amounts"].get_str();
     100           0 :     std::string strProposalHashes = obj["proposal_hashes"].get_str();
     101           0 :     ParsePaymentSchedule(strAddresses, strAmounts, strProposalHashes);
     102             : 
     103           0 :     LogPrint(BCLog::GOBJECT, "CSuperblock -- nBlockHeight = %d, strAddresses = %s, strAmounts = %s, vecPayments.size() = %d\n",
     104             :         nBlockHeight, strAddresses, strAmounts, vecPayments.size());
     105           0 : }
     106             : 
     107           2 : CSuperblock::CSuperblock(int nBlockHeight, std::vector<CGovernancePayment> vecPayments) : nBlockHeight(nBlockHeight), vecPayments(std::move(vecPayments))
     108           1 : {
     109           1 :     nStatus = SeenObjectStatus::Valid; //TODO: Investigate this
     110           1 :     nGovObjHash = GetHash();
     111           2 : }
     112             : 
     113             : /**
     114             :  *   Is Valid Superblock Height
     115             :  *
     116             :  *   - See if a block at this height can be a superblock
     117             :  */
     118             : 
     119       28050 : bool CSuperblock::IsValidBlockHeight(int nBlockHeight)
     120             : {
     121             :     // SUPERBLOCKS CAN HAPPEN ONLY after hardfork and only ONCE PER CYCLE
     122       32487 :     return nBlockHeight >= Params().GetConsensus().nSuperblockStartBlock &&
     123        4437 :            ((nBlockHeight % Params().GetConsensus().nSuperblockCycle) == 0);
     124             : }
     125             : 
     126           0 : void CSuperblock::GetNearestSuperblocksHeights(int nBlockHeight, int& nLastSuperblockRet, int& nNextSuperblockRet)
     127             : {
     128           0 :     const Consensus::Params& consensusParams = Params().GetConsensus();
     129           0 :     int nSuperblockStartBlock = consensusParams.nSuperblockStartBlock;
     130           0 :     int nSuperblockCycle = consensusParams.nSuperblockCycle;
     131             : 
     132             :     // Get first superblock
     133           0 :     int nFirstSuperblockOffset = (nSuperblockCycle - nSuperblockStartBlock % nSuperblockCycle) % nSuperblockCycle;
     134           0 :     int nFirstSuperblock = nSuperblockStartBlock + nFirstSuperblockOffset;
     135             : 
     136           0 :     if (nBlockHeight < nFirstSuperblock) {
     137           0 :         nLastSuperblockRet = 0;
     138           0 :         nNextSuperblockRet = nFirstSuperblock;
     139           0 :     } else {
     140           0 :         nLastSuperblockRet = nBlockHeight - nBlockHeight % nSuperblockCycle;
     141           0 :         nNextSuperblockRet = nLastSuperblockRet + nSuperblockCycle;
     142             :     }
     143           0 : }
     144             : 
     145        1766 : CAmount CSuperblock::GetPaymentsLimit(const CChain& active_chain, int nBlockHeight)
     146             : {
     147        1766 :     const Consensus::Params& consensusParams = Params().GetConsensus();
     148             : 
     149        1766 :     if (!IsValidBlockHeight(nBlockHeight)) {
     150        1677 :         return 0;
     151             :     }
     152             : 
     153          89 :     const bool fV20Active{nBlockHeight >= consensusParams.V20Height};
     154             : 
     155             :     // min subsidy for high diff networks and vice versa
     156          89 :     int nBits = consensusParams.fPowAllowMinDifficultyBlocks ? UintToArith256(consensusParams.powLimit).GetCompact() : 1;
     157             :     // some part of all blocks issued during the cycle goes to superblock, see GetBlockSubsidy
     158          89 :     CAmount nSuperblockPartOfSubsidy = GetSuperblockSubsidyInner(nBits, nBlockHeight - 1, consensusParams, fV20Active);
     159          89 :     CAmount nPaymentsLimit = nSuperblockPartOfSubsidy * consensusParams.nSuperblockCycle;
     160          89 :     LogPrint(BCLog::GOBJECT, "CSuperblock::GetPaymentsLimit -- Valid superblock height %d, payments max %lld\n", nBlockHeight, nPaymentsLimit);
     161             : 
     162          89 :     return nPaymentsLimit;
     163        1766 : }
     164             : 
     165           0 : void CSuperblock::ParsePaymentSchedule(const std::string& strPaymentAddresses, const std::string& strPaymentAmounts, const std::string& strProposalHashes)
     166             : {
     167             :     // SPLIT UP ADDR/AMOUNT STRINGS AND PUT IN VECTORS
     168             : 
     169           0 :     const auto vecPaymentAddresses = SplitString(strPaymentAddresses, "|");
     170           0 :     const auto vecPaymentAmounts = SplitString(strPaymentAmounts, "|");
     171           0 :     const auto vecProposalHashes = SplitString(strProposalHashes, "|");
     172             : 
     173             :     // IF THESE DON'T MATCH, SOMETHING IS WRONG
     174             : 
     175           0 :     if (vecPaymentAddresses.size() != vecPaymentAmounts.size() || vecPaymentAddresses.size() != vecProposalHashes.size()) {
     176           0 :         std::string msg{strprintf("CSuperblock::%s -- Mismatched payments, amounts and proposalHashes", __func__)};
     177           0 :         LogPrintf("%s\n", msg);
     178           0 :         throw std::runtime_error(msg);
     179           0 :     }
     180             : 
     181           0 :     if (vecPaymentAddresses.empty()) {
     182           0 :         std::string msg{strprintf("CSuperblock::%s -- Error no payments", __func__)};
     183           0 :         LogPrintf("%s\n", msg);
     184           0 :         throw std::runtime_error(msg);
     185           0 :     }
     186             : 
     187             :     // LOOP THROUGH THE ADDRESSES/AMOUNTS AND CREATE PAYMENTS
     188             :     /*
     189             :       ADDRESSES = [ADDR1|2|3|4|5|6]
     190             :       AMOUNTS = [AMOUNT1|2|3|4|5|6]
     191             :     */
     192             : 
     193           0 :     for (int i = 0; i < (int)vecPaymentAddresses.size(); i++) {
     194           0 :         CTxDestination dest = DecodeDestination(vecPaymentAddresses[i]);
     195           0 :         if (!IsValidDestination(dest)) {
     196           0 :             std::string msg{strprintf("CSuperblock::%s -- Invalid Dash Address: %s", __func__, vecPaymentAddresses[i])};
     197           0 :             LogPrintf("%s\n", msg);
     198           0 :             throw std::runtime_error(msg);
     199           0 :         }
     200             : 
     201           0 :         CAmount nAmount = ParsePaymentAmount(vecPaymentAmounts[i]);
     202             : 
     203           0 :         uint256 proposalHash;
     204           0 :         if (!ParseHashStr(vecProposalHashes[i], proposalHash)) {
     205           0 :             std::string msg{strprintf("CSuperblock::%s -- Invalid proposal hash: %s", __func__, vecProposalHashes[i])};
     206           0 :             LogPrintf("%s\n", msg);
     207           0 :             throw std::runtime_error(msg);
     208           0 :         }
     209             : 
     210           0 :         LogPrint(BCLog::GOBJECT, /* Continued */
     211             :                  "CSuperblock::%s -- i = %d, amount string = %s, nAmount = %lld, proposalHash = %s\n", __func__,
     212             :                  i, vecPaymentAmounts[i], nAmount, proposalHash.ToString());
     213             : 
     214           0 :         CGovernancePayment payment(dest, nAmount, proposalHash);
     215           0 :         if (payment.IsValid()) {
     216           0 :             vecPayments.push_back(payment);
     217           0 :         } else {
     218           0 :             vecPayments.clear();
     219           0 :             std::string msg{strprintf("CSuperblock::%s -- Invalid payment found: address = %s, amount = %d", __func__,
     220           0 :                 EncodeDestination(dest), nAmount)};
     221           0 :             LogPrintf("%s\n", msg);
     222           0 :             throw std::runtime_error(msg);
     223           0 :         }
     224           0 :     }
     225           0 : }
     226             : 
     227           6 : bool CSuperblock::GetPayment(int nPaymentIndex, CGovernancePayment& paymentRet)
     228             : {
     229           6 :     if ((nPaymentIndex < 0) || (nPaymentIndex >= (int)vecPayments.size())) {
     230           0 :         return false;
     231             :     }
     232             : 
     233           6 :     paymentRet = vecPayments[nPaymentIndex];
     234           6 :     return true;
     235           6 : }
     236             : 
     237           3 : CAmount CSuperblock::GetPaymentsTotalAmount()
     238             : {
     239           9 :     return std23::ranges::fold_left(vecPayments, CAmount{0}, [](CAmount s, const auto& p) { return s + p.nAmount; });
     240             : }
     241             : 
     242             : /**
     243             : *   Is Transaction Valid
     244             : *
     245             : *   - Does this transaction match the superblock?
     246             : */
     247             : 
     248           3 : bool CSuperblock::IsValid(const CChain& active_chain, const CTransaction& txNew, int nBlockHeight, CAmount blockReward, bool is_v24)
     249             : {
     250             :     // TODO : LOCK(cs);
     251             :     // No reason for a lock here now since this method only accesses data
     252             :     // internal to *this and since CSuperblock's are accessed only through
     253             :     // shared pointers there's no way our object can get deleted while this
     254             :     // code is running.
     255           3 :     if (!IsValidBlockHeight(nBlockHeight)) {
     256           0 :         LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid, incorrect block height\n");
     257           0 :         return false;
     258             :     }
     259             : 
     260             :     // CONFIGURE SUPERBLOCK OUTPUTS
     261             : 
     262           3 :     int nOutputs = txNew.vout.size();
     263           3 :     int nPayments = CountPayments();
     264           3 :     int nMinerAndMasternodePayments = nOutputs - nPayments;
     265             : 
     266           3 :     LogPrint(BCLog::GOBJECT, "CSuperblock::IsValid -- nOutputs = %d, nPayments = %d, hash = %s\n", nOutputs, nPayments,
     267             :              nGovObjHash.ToString());
     268             : 
     269             :     // We require an exact match (including order) between the expected
     270             :     // superblock payments and the payments actually in the block.
     271             : 
     272           3 :     if (nMinerAndMasternodePayments < 0) {
     273             :         // This means the block cannot have all the superblock payments
     274             :         // so it is not valid.
     275             :         // TODO: could that be that we just hit coinbase size limit?
     276           0 :         LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid, too few superblock payments\n");
     277           0 :         return false;
     278             :     }
     279             : 
     280             :     // payments should not exceed limit
     281           3 :     CAmount nPaymentsTotalAmount = GetPaymentsTotalAmount();
     282           3 :     CAmount nPaymentsLimit = GetPaymentsLimit(active_chain, nBlockHeight);
     283           3 :     if (nPaymentsTotalAmount > nPaymentsLimit) {
     284           0 :         LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid, payments limit exceeded: payments %lld, limit %lld\n", nPaymentsTotalAmount, nPaymentsLimit);
     285           0 :         return false;
     286             :     }
     287             : 
     288             :     // miner and masternodes should not get more than they would usually get
     289           3 :     CAmount nBlockValue = txNew.GetValueOut();
     290           3 :     if (nBlockValue > blockReward + nPaymentsTotalAmount) {
     291           0 :         LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid, block value limit exceeded: block %lld, limit %lld\n", nBlockValue, blockReward + nPaymentsTotalAmount);
     292           0 :         return false;
     293             :     }
     294             : 
     295           3 :     int nVoutIndex = -1;
     296           8 :     for (int i = 0; i < nPayments; i++) {
     297           6 :         CGovernancePayment payment;
     298           6 :         if (!GetPayment(i, payment)) {
     299             :             // This shouldn't happen so log a warning
     300           0 :             LogPrintf("CSuperblock::IsValid -- WARNING: Failed to find payment: %d of %d total payments\n", i, nPayments);
     301           0 :             continue;
     302             :         }
     303             : 
     304           6 :         bool fPaymentMatch = false;
     305             : 
     306             :         // From V24 on, start past the previously matched output so each expected
     307             :         // payment consumes a distinct vout (two adjacent payments with the same
     308             :         // script and amount must match two separate outputs, not the same one
     309             :         // twice). Before V24 the scan restarted at the previously matched index
     310             :         // (inclusive), which is kept for backwards compatibility.
     311             :         // TODO: After V24 is hardened/finalized so historical duplicate-output
     312             :         // blocks cannot be encountered, simplify this path to the V24 scan only.
     313           6 :         const int nVoutStart = is_v24 ? nVoutIndex + 1 : std::max(nVoutIndex, 0);
     314           9 :         for (int j = nVoutStart; j < nOutputs; j++) {
     315             :             // Find superblock payment
     316          13 :             fPaymentMatch = ((payment.script == txNew.vout[j].scriptPubKey) &&
     317           5 :                              (payment.nAmount == txNew.vout[j].nValue));
     318             : 
     319           8 :             if (fPaymentMatch) {
     320           5 :                 nVoutIndex = j;
     321           5 :                 break;
     322             :             }
     323           3 :         }
     324             : 
     325           6 :         if (!fPaymentMatch) {
     326             :             // Superblock payment not found!
     327             : 
     328           1 :             CTxDestination dest;
     329           1 :             ExtractDestination(payment.script, dest);
     330           1 :             LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid: %d payment %d to %s not found\n", i, payment.nAmount, EncodeDestination(dest));
     331             : 
     332           1 :             return false;
     333             :         }
     334           6 :     }
     335             : 
     336           2 :     return true;
     337           3 : }
     338             : 
     339           0 : bool CSuperblock::IsExpired(int heightToTest) const
     340             : {
     341             :     int nExpirationBlocks;
     342             :     // Executed triggers are kept for another superblock cycle (approximately 1 month for mainnet).
     343             :     // Other valid triggers are kept for ~1 day only (for mainnet, but no longer than a superblock cycle for other networks).
     344             :     // Everything else is pruned after ~1h (for mainnet, but no longer than a superblock cycle for other networks).
     345           0 :     switch (nStatus) {
     346             :     case SeenObjectStatus::Executed:
     347           0 :         nExpirationBlocks = Params().GetConsensus().nSuperblockCycle;
     348           0 :         break;
     349             :     case SeenObjectStatus::Valid:
     350           0 :         nExpirationBlocks = std::min(576, Params().GetConsensus().nSuperblockCycle);
     351           0 :         break;
     352             :     default:
     353           0 :         nExpirationBlocks = std::min(24, Params().GetConsensus().nSuperblockCycle);
     354           0 :         break;
     355             :     }
     356             : 
     357           0 :     int nExpirationBlock = nBlockHeight + nExpirationBlocks;
     358             : 
     359           0 :     LogPrint(BCLog::GOBJECT, "CSuperblock::IsExpired -- nBlockHeight = %d, nExpirationBlock = %d\n", nBlockHeight, nExpirationBlock);
     360             : 
     361           0 :     if (heightToTest > nExpirationBlock) {
     362           0 :         LogPrint(BCLog::GOBJECT, "CSuperblock::IsExpired -- Outdated trigger found\n");
     363           0 :         return true;
     364             :     }
     365             : 
     366           0 :     if (Params().NetworkIDString() != CBaseChainParams::MAIN) {
     367             :         // NOTE: this can happen on testnet/devnets due to reorgs, should never happen on mainnet
     368           0 :         if (heightToTest + Params().GetConsensus().nSuperblockCycle * 2 < nBlockHeight) {
     369           0 :             LogPrint(BCLog::GOBJECT, "CSuperblock::IsExpired -- Trigger is too far into the future\n");
     370           0 :             return true;
     371             :         }
     372           0 :     }
     373             : 
     374           0 :     return false;
     375           0 : }
     376             : 
     377           0 : std::vector<uint256> CSuperblock::GetProposalHashes() const
     378             : {
     379           0 :     std::vector<uint256> res;
     380             : 
     381           0 :     for (const auto& payment : vecPayments) {
     382           0 :         res.push_back(payment.proposalHash);
     383             :     }
     384             : 
     385           0 :     return res;
     386           0 : }
     387             : 
     388           0 : std::string CSuperblock::GetHexStrData() const
     389             : {
     390             :     // {\"event_block_height\": 879720, \"payment_addresses\": \"yd5KMREs3GLMe6mTJYr3YrH1juwNwrFCfB\", \"payment_amounts\": \"5.00000000\", \"proposal_hashes\": \"485817fddbcab6c55c9a6856dabc8b19ed79548bda8c01712daebc9f74f287f4\", \"type\": 2}
     391             : 
     392           0 :     std::string str_addresses = Join(vecPayments, "|", [&](const auto& payment) {
     393           0 :         CTxDestination dest;
     394           0 :         ExtractDestination(payment.script, dest);
     395           0 :         return EncodeDestination(dest);
     396             :     });
     397           0 :     std::string str_amounts = Join(vecPayments, "|", [&](const auto& payment) {
     398           0 :         return ValueFromAmount(payment.nAmount).write();
     399           0 :     });
     400           0 :     std::string str_hashes = Join(vecPayments, "|", [&](const auto& payment) { return payment.proposalHash.ToString(); });
     401             : 
     402           0 :     std::stringstream ss;
     403           0 :     ss << "{";
     404           0 :     ss << "\"event_block_height\": " << nBlockHeight << ", ";
     405           0 :     ss << "\"payment_addresses\": \"" << str_addresses << "\", ";
     406           0 :     ss << "\"payment_amounts\": \"" << str_amounts << "\", ";
     407           0 :     ss << "\"proposal_hashes\": \"" << str_hashes << "\", ";
     408           0 :     ss << "\"type\":" << 2;
     409           0 :     ss << "}";
     410             : 
     411           0 :     return HexStr(ss.str());
     412           0 : }
     413             : 
     414           4 : CGovernancePayment::CGovernancePayment(const CTxDestination& destIn, CAmount nAmountIn, const uint256& proposalHash) :
     415           2 :         fValid(false),
     416           2 :         script(),
     417           2 :         nAmount(0),
     418           2 :         proposalHash(proposalHash)
     419           2 : {
     420             :     try {
     421           2 :         script = GetScriptForDestination(destIn);
     422           2 :         nAmount = nAmountIn;
     423           2 :         fValid = true;
     424           2 :     } catch (std::exception& e) {
     425           0 :         LogPrintf("CGovernancePayment Payment not valid: destIn = %s, nAmountIn = %d, what = %s\n",
     426             :                   EncodeDestination(destIn), nAmountIn, e.what());
     427           0 :     } catch (...) {
     428           0 :         LogPrintf("CGovernancePayment Payment not valid: destIn = %s, nAmountIn = %d\n",
     429             :                   EncodeDestination(destIn), nAmountIn);
     430           0 :     }
     431           4 : }
     432             : 
     433             : namespace governance {
     434             : 
     435           0 : bool SuperblockManager::AddTrigger(std::shared_ptr<CGovernanceObject> obj, int cachedHeight)
     436             : {
     437           0 :     AssertLockNotHeld(cs_sb);
     438           0 :     if (!obj) return false;
     439             : 
     440           0 :     uint256 nHash = obj->GetHash();
     441             : 
     442           0 :     LOCK(cs_sb);
     443           0 :     if (m_triggers.count(nHash)) {
     444           0 :         LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- Already have hash, nHash = %s, size = %s\n", __func__,
     445             :                  nHash.GetHex(), m_triggers.size());
     446           0 :         return false;
     447             :     }
     448             : 
     449           0 :     CSuperblock_sptr pSuperblock;
     450             :     try {
     451           0 :         pSuperblock = std::make_shared<CSuperblock>(*obj, nHash);
     452           0 :     } catch (std::exception& e) {
     453           0 :         LogPrintf("SuperblockManager::%s -- Error creating superblock: %s\n", __func__, e.what());
     454           0 :         return false;
     455           0 :     } catch (...) {
     456           0 :         LogPrintf("SuperblockManager::%s -- Unknown Error creating superblock\n", __func__);
     457           0 :         return false;
     458           0 :     }
     459             : 
     460           0 :     pSuperblock->SetStatus(SeenObjectStatus::Valid);
     461           0 :     m_triggers.emplace(nHash, TriggerEntry{pSuperblock, std::move(obj)});
     462             : 
     463           0 :     return !pSuperblock->IsExpired(cachedHeight);
     464           0 : }
     465             : 
     466           0 : void SuperblockManager::RemoveTrigger(const uint256& hash)
     467             : {
     468           0 :     LOCK(cs_sb);
     469           0 :     m_triggers.erase(hash);
     470           0 : }
     471             : 
     472           0 : void SuperblockManager::Clean(int cachedHeight)
     473             : {
     474           0 :     AssertLockNotHeld(cs_sb);
     475           0 :     LOCK(cs_sb);
     476             : 
     477           0 :     LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- m_triggers.size() = %d\n", __func__, m_triggers.size());
     478             : 
     479           0 :     for (auto it = m_triggers.begin(); it != m_triggers.end();) {
     480           0 :         bool remove = false;
     481           0 :         const auto& [sb, obj] = it->second;
     482             : 
     483           0 :         if (!sb) {
     484           0 :             LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- nullptr superblock\n", __func__);
     485           0 :             remove = true;
     486           0 :         } else {
     487           0 :             if (!obj || obj->GetObjectType() != GovernanceObject::TRIGGER) {
     488           0 :                 LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- Unknown or non-trigger superblock\n", __func__);
     489           0 :                 sb->SetStatus(SeenObjectStatus::ErrorInvalid);
     490           0 :             }
     491           0 :             LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- superblock status = %d\n", __func__,
     492             :                      std23::to_underlying(sb->GetStatus()));
     493           0 :             switch (sb->GetStatus()) {
     494             :             case SeenObjectStatus::ErrorInvalid:
     495             :             case SeenObjectStatus::Unknown:
     496           0 :                 LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- Unknown or invalid trigger found\n", __func__);
     497           0 :                 remove = true;
     498           0 :                 break;
     499             :             case SeenObjectStatus::Valid:
     500             :             case SeenObjectStatus::Executed:
     501           0 :                 LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- Valid trigger found\n", __func__);
     502           0 :                 if (sb->IsExpired(cachedHeight)) {
     503           0 :                     if (obj) obj->SetExpired();
     504           0 :                     remove = true;
     505           0 :                 }
     506           0 :                 break;
     507             :             default:
     508           0 :                 break;
     509             :             }
     510             :         }
     511           0 :         LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- %smarked for removal\n", __func__, remove ? "" : "NOT ");
     512             : 
     513           0 :         if (remove) {
     514           0 :             std::string strDataAsPlainString = "nullptr";
     515           0 :             if (obj) {
     516           0 :                 strDataAsPlainString = obj->GetDataAsPlainString();
     517           0 :                 obj->PrepareDeletion(GetTime<std::chrono::seconds>().count());
     518           0 :             }
     519           0 :             LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- Removing trigger object %s\n", __func__,
     520             :                      strDataAsPlainString);
     521           0 :             it = m_triggers.erase(it);
     522           0 :         } else {
     523           0 :             ++it;
     524             :         }
     525             :     }
     526           0 : }
     527             : 
     528         178 : void SuperblockManager::Clear()
     529             : {
     530         178 :     LOCK(cs_sb);
     531         178 :     m_triggers.clear();
     532         178 :     m_loaded = false;
     533         178 : }
     534             : 
     535           0 : std::vector<CSuperblock_sptr> SuperblockManager::GetActiveTriggers() const
     536             : {
     537           0 :     LOCK(cs_sb);
     538           0 :     std::vector<CSuperblock_sptr> vecResults;
     539           0 :     vecResults.reserve(m_triggers.size());
     540           0 :     for (const auto& [_, entry] : m_triggers) {
     541           0 :         if (entry.sb && entry.obj) {
     542           0 :             vecResults.push_back(entry.sb);
     543           0 :         }
     544             :     }
     545           0 :     return vecResults;
     546           0 : }
     547             : 
     548           0 : bool SuperblockManager::GetBestSuperblock(const CDeterministicMNList& tip_mn_list, CSuperblock_sptr& sbRet,
     549             :                                           int nBlockHeight) const
     550             : {
     551           0 :     LOCK(cs_sb);
     552           0 :     return GetBestSuperblockInternal(tip_mn_list, sbRet, nBlockHeight);
     553           0 : }
     554             : 
     555           0 : bool SuperblockManager::GetBestSuperblockInternal(const CDeterministicMNList& tip_mn_list, CSuperblock_sptr& sbRet,
     556             :                                                   int nBlockHeight) const
     557             : {
     558           0 :     AssertLockHeld(cs_sb);
     559           0 :     if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) {
     560           0 :         return false;
     561             :     }
     562             : 
     563           0 :     int nYesCount = 0;
     564           0 :     for (const auto& [_, entry] : m_triggers) {
     565           0 :         if (!entry.sb || !entry.obj || nBlockHeight != entry.sb->GetBlockHeight()) {
     566           0 :             continue;
     567             :         }
     568           0 :         int nTempYesCount = entry.obj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
     569           0 :         if (nTempYesCount > nYesCount) {
     570           0 :             nYesCount = nTempYesCount;
     571           0 :             sbRet = entry.sb;
     572           0 :         }
     573             :     }
     574           0 :     return nYesCount > 0;
     575           0 : }
     576             : 
     577       24517 : bool SuperblockManager::IsSuperblockTriggered(const CDeterministicMNList& tip_mn_list, int nBlockHeight)
     578             : {
     579       24517 :     LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- Start nBlockHeight = %d\n", nBlockHeight);
     580       24517 :     if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) {
     581       24474 :         return false;
     582             :     }
     583             : 
     584          43 :     LOCK(cs_sb);
     585             : 
     586          43 :     LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- m_triggers.size() = %d\n", m_triggers.size());
     587             : 
     588          43 :     for (const auto& [_, entry] : m_triggers) {
     589           0 :         if (!entry.sb) {
     590           0 :             LogPrintf("IsSuperblockTriggered -- Non-superblock found, continuing\n");
     591           0 :             continue;
     592             :         }
     593           0 :         if (!entry.obj) {
     594           0 :             LogPrintf("IsSuperblockTriggered -- pObj == nullptr, continuing\n");
     595           0 :             continue;
     596             :         }
     597             : 
     598           0 :         LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- data = %s\n", entry.obj->GetDataAsPlainString());
     599             : 
     600           0 :         if (nBlockHeight != entry.sb->GetBlockHeight()) {
     601           0 :             LogPrint(BCLog::GOBJECT, /* Continued */
     602             :                      "IsSuperblockTriggered -- block height doesn't match nBlockHeight = %d, blockStart = %d, "
     603             :                      "continuing\n",
     604             :                      nBlockHeight, entry.sb->GetBlockHeight());
     605           0 :             continue;
     606             :         }
     607             : 
     608           0 :         entry.obj->UpdateSentinelVariables(tip_mn_list);
     609             : 
     610           0 :         if (entry.obj->IsSetCachedFunding()) {
     611           0 :             LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- fCacheFunding = true, returning true\n");
     612           0 :             return true;
     613             :         } else {
     614           0 :             LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- fCacheFunding = false, continuing\n");
     615             :         }
     616             :     }
     617             : 
     618          43 :     return false;
     619       24517 : }
     620             : 
     621           0 : bool SuperblockManager::IsValidSuperblock(const CChain& active_chain, const CDeterministicMNList& tip_mn_list,
     622             :                                           const CTransaction& txNew, int nBlockHeight, CAmount blockReward, bool is_v24) const
     623             : {
     624           0 :     LOCK(cs_sb);
     625           0 :     CSuperblock_sptr pSuperblock;
     626           0 :     if (GetBestSuperblockInternal(tip_mn_list, pSuperblock, nBlockHeight)) {
     627           0 :         return pSuperblock->IsValid(active_chain, txNew, nBlockHeight, blockReward, is_v24);
     628             :     }
     629           0 :     return false;
     630           0 : }
     631             : 
     632           0 : bool SuperblockManager::GetSuperblockPayments(const CDeterministicMNList& tip_mn_list, int nBlockHeight,
     633             :                                               std::vector<CTxOut>& voutSuperblockRet) const
     634             : {
     635           0 :     LOCK(cs_sb);
     636             : 
     637           0 :     CSuperblock_sptr pSuperblock;
     638           0 :     if (!GetBestSuperblockInternal(tip_mn_list, pSuperblock, nBlockHeight)) {
     639           0 :         LogPrint(BCLog::GOBJECT, "GetSuperblockPayments -- Can't find superblock for height %d\n", nBlockHeight);
     640           0 :         return false;
     641             :     }
     642             : 
     643           0 :     voutSuperblockRet.clear();
     644             : 
     645             :     // TODO: How many payments can we add before things blow up?
     646             :     //       Consider at least following limits:
     647             :     //          - max coinbase tx size
     648             :     //          - max "budget" available
     649           0 :     for (int i = 0; i < pSuperblock->CountPayments(); i++) {
     650           0 :         CGovernancePayment payment;
     651           0 :         if (pSuperblock->GetPayment(i, payment)) {
     652           0 :             voutSuperblockRet.emplace_back(payment.nAmount, payment.script);
     653             : 
     654           0 :             CTxDestination dest;
     655           0 :             ExtractDestination(payment.script, dest);
     656             : 
     657           0 :             LogPrint(BCLog::GOBJECT, "GetSuperblockPayments -- NEW Superblock: output %d (addr %s, amount %d.%08d)\n",
     658             :                      i, EncodeDestination(dest), payment.nAmount / COIN, payment.nAmount % COIN);
     659           0 :         } else {
     660           0 :             LogPrint(BCLog::GOBJECT, "GetSuperblockPayments -- Payment not found\n");
     661             :         }
     662           0 :     }
     663             : 
     664           0 :     return true;
     665           0 : }
     666             : 
     667           0 : void SuperblockManager::ExecuteBestSuperblock(const CDeterministicMNList& tip_mn_list, int nBlockHeight)
     668             : {
     669           0 :     LOCK(cs_sb);
     670           0 :     CSuperblock_sptr pSuperblock;
     671           0 :     if (GetBestSuperblockInternal(tip_mn_list, pSuperblock, nBlockHeight)) {
     672             :         // All checks are done in CSuperblock::IsValid via IsBlockValueValid and IsBlockPayeeValid,
     673             :         // tip wouldn't be updated if anything was wrong. Mark this trigger as executed.
     674           0 :         pSuperblock->SetExecuted();
     675           0 :     }
     676           0 : }
     677             : 
     678             : } // namespace governance

Generated by: LCOV version 1.16