LCOV - code coverage report
Current view: top level - src/governance - superblock.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 291 387 75.2 %
Date: 2026-06-25 07:23:43 Functions: 31 34 91.2 %

          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         952 : CAmount ParsePaymentAmount(const std::string& strAmount)
      23             : {
      24         952 :     CAmount nAmount = 0;
      25         952 :     if (strAmount.empty()) {
      26           0 :         throw std::runtime_error(strprintf("%s -- Amount is empty", __func__));
      27             :     }
      28         952 :     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         952 :     size_t pos = strAmount.find_first_not_of("0123456789.");
      37         952 :     if (pos != std::string::npos) {
      38           0 :         throw std::runtime_error(strprintf("%s -- Amount string contains invalid character", __func__));
      39             :     }
      40             : 
      41         952 :     pos = strAmount.find('.');
      42         952 :     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         952 :     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         952 :     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         952 :     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         952 :     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         376 : CSuperblock::CSuperblock(const CGovernanceObject& govObj, uint256& nHash) :
      76         188 :     nGovObjHash(nHash),
      77         188 :     nBlockHeight(0),
      78         188 :     nStatus(SeenObjectStatus::Unknown),
      79         188 :     vecPayments()
      80         188 : {
      81         188 :     LogPrint(BCLog::GOBJECT, "CSuperblock -- Constructor govobj: %s, nObjectType = %d\n", govObj.GetDataAsPlainString(),
      82             :              std23::to_underlying(govObj.GetObjectType()));
      83             : 
      84         188 :     if (govObj.GetObjectType() != GovernanceObject::TRIGGER) {
      85           0 :         throw std::runtime_error("CSuperblock: Governance Object not a trigger");
      86             :     }
      87             : 
      88         188 :     UniValue obj = govObj.GetJSONObject();
      89             : 
      90         188 :     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         188 :     nBlockHeight = obj["event_block_height"].getInt<int>();
      96             : 
      97             :     // NEXT WE GET THE PAYMENT INFORMATION AND RECONSTRUCT THE PAYMENT VECTOR
      98         188 :     std::string strAddresses = obj["payment_addresses"].get_str();
      99         188 :     std::string strAmounts = obj["payment_amounts"].get_str();
     100         188 :     std::string strProposalHashes = obj["proposal_hashes"].get_str();
     101         188 :     ParsePaymentSchedule(strAddresses, strAmounts, strProposalHashes);
     102             : 
     103         188 :     LogPrint(BCLog::GOBJECT, "CSuperblock -- nBlockHeight = %d, strAddresses = %s, strAmounts = %s, vecPayments.size() = %d\n",
     104             :         nBlockHeight, strAddresses, strAmounts, vecPayments.size());
     105         376 : }
     106             : 
     107         410 : CSuperblock::CSuperblock(int nBlockHeight, std::vector<CGovernancePayment> vecPayments) : nBlockHeight(nBlockHeight), vecPayments(std::move(vecPayments))
     108         205 : {
     109         205 :     nStatus = SeenObjectStatus::Valid; //TODO: Investigate this
     110         205 :     nGovObjHash = GetHash();
     111         410 : }
     112             : 
     113             : /**
     114             :  *   Is Valid Superblock Height
     115             :  *
     116             :  *   - See if a block at this height can be a superblock
     117             :  */
     118             : 
     119      450265 : bool CSuperblock::IsValidBlockHeight(int nBlockHeight)
     120             : {
     121             :     // SUPERBLOCKS CAN HAPPEN ONLY after hardfork and only ONCE PER CYCLE
     122      567219 :     return nBlockHeight >= Params().GetConsensus().nSuperblockStartBlock &&
     123      116954 :            ((nBlockHeight % Params().GetConsensus().nSuperblockCycle) == 0);
     124             : }
     125             : 
     126         208 : void CSuperblock::GetNearestSuperblocksHeights(int nBlockHeight, int& nLastSuperblockRet, int& nNextSuperblockRet)
     127             : {
     128         208 :     const Consensus::Params& consensusParams = Params().GetConsensus();
     129         208 :     int nSuperblockStartBlock = consensusParams.nSuperblockStartBlock;
     130         208 :     int nSuperblockCycle = consensusParams.nSuperblockCycle;
     131             : 
     132             :     // Get first superblock
     133         208 :     int nFirstSuperblockOffset = (nSuperblockCycle - nSuperblockStartBlock % nSuperblockCycle) % nSuperblockCycle;
     134         208 :     int nFirstSuperblock = nSuperblockStartBlock + nFirstSuperblockOffset;
     135             : 
     136         208 :     if (nBlockHeight < nFirstSuperblock) {
     137           0 :         nLastSuperblockRet = 0;
     138           0 :         nNextSuperblockRet = nFirstSuperblock;
     139           0 :     } else {
     140         208 :         nLastSuperblockRet = nBlockHeight - nBlockHeight % nSuperblockCycle;
     141         208 :         nNextSuperblockRet = nLastSuperblockRet + nSuperblockCycle;
     142             :     }
     143         208 : }
     144             : 
     145       27823 : CAmount CSuperblock::GetPaymentsLimit(const CChain& active_chain, int nBlockHeight)
     146             : {
     147       27823 :     const Consensus::Params& consensusParams = Params().GetConsensus();
     148             : 
     149       27823 :     if (!IsValidBlockHeight(nBlockHeight)) {
     150       25785 :         return 0;
     151             :     }
     152             : 
     153        2038 :     const bool fV20Active{nBlockHeight >= consensusParams.V20Height};
     154             : 
     155             :     // min subsidy for high diff networks and vice versa
     156        2038 :     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        2038 :     CAmount nSuperblockPartOfSubsidy = GetSuperblockSubsidyInner(nBits, nBlockHeight - 1, consensusParams, fV20Active);
     159        2038 :     CAmount nPaymentsLimit = nSuperblockPartOfSubsidy * consensusParams.nSuperblockCycle;
     160        2038 :     LogPrint(BCLog::GOBJECT, "CSuperblock::GetPaymentsLimit -- Valid superblock height %d, payments max %lld\n", nBlockHeight, nPaymentsLimit);
     161             : 
     162        2038 :     return nPaymentsLimit;
     163       27823 : }
     164             : 
     165         188 : 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         188 :     const auto vecPaymentAddresses = SplitString(strPaymentAddresses, "|");
     170         188 :     const auto vecPaymentAmounts = SplitString(strPaymentAmounts, "|");
     171         188 :     const auto vecProposalHashes = SplitString(strProposalHashes, "|");
     172             : 
     173             :     // IF THESE DON'T MATCH, SOMETHING IS WRONG
     174             : 
     175         188 :     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         188 :     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         564 :     for (int i = 0; i < (int)vecPaymentAddresses.size(); i++) {
     194         376 :         CTxDestination dest = DecodeDestination(vecPaymentAddresses[i]);
     195         376 :         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         376 :         CAmount nAmount = ParsePaymentAmount(vecPaymentAmounts[i]);
     202             : 
     203         376 :         uint256 proposalHash;
     204         376 :         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         376 :         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         376 :         CGovernancePayment payment(dest, nAmount, proposalHash);
     215         376 :         if (payment.IsValid()) {
     216         376 :             vecPayments.push_back(payment);
     217         376 :         } 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         376 :     }
     225         188 : }
     226             : 
     227         500 : bool CSuperblock::GetPayment(int nPaymentIndex, CGovernancePayment& paymentRet)
     228             : {
     229         500 :     if ((nPaymentIndex < 0) || (nPaymentIndex >= (int)vecPayments.size())) {
     230           0 :         return false;
     231             :     }
     232             : 
     233         500 :     paymentRet = vecPayments[nPaymentIndex];
     234         500 :     return true;
     235         500 : }
     236             : 
     237         233 : CAmount CSuperblock::GetPaymentsTotalAmount()
     238             : {
     239         699 :     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         233 : 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         233 :     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         233 :     int nOutputs = txNew.vout.size();
     263         233 :     int nPayments = CountPayments();
     264         233 :     int nMinerAndMasternodePayments = nOutputs - nPayments;
     265             : 
     266         233 :     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         233 :     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         233 :     CAmount nPaymentsTotalAmount = GetPaymentsTotalAmount();
     282         233 :     CAmount nPaymentsLimit = GetPaymentsLimit(active_chain, nBlockHeight);
     283         233 :     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         233 :     CAmount nBlockValue = txNew.GetValueOut();
     290         233 :     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         233 :     int nVoutIndex = -1;
     296         698 :     for (int i = 0; i < nPayments; i++) {
     297         466 :         CGovernancePayment payment;
     298         466 :         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         466 :         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         466 :         const int nVoutStart = is_v24 ? nVoutIndex + 1 : std::max(nVoutIndex, 0);
     314        1619 :         for (int j = nVoutStart; j < nOutputs; j++) {
     315             :             // Find superblock payment
     316        2083 :             fPaymentMatch = ((payment.script == txNew.vout[j].scriptPubKey) &&
     317         465 :                              (payment.nAmount == txNew.vout[j].nValue));
     318             : 
     319        1618 :             if (fPaymentMatch) {
     320         465 :                 nVoutIndex = j;
     321         465 :                 break;
     322             :             }
     323        1153 :         }
     324             : 
     325         466 :         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         466 :     }
     335             : 
     336         232 :     return true;
     337         233 : }
     338             : 
     339        5228 : 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        5228 :     switch (nStatus) {
     346             :     case SeenObjectStatus::Executed:
     347        1946 :         nExpirationBlocks = Params().GetConsensus().nSuperblockCycle;
     348        1946 :         break;
     349             :     case SeenObjectStatus::Valid:
     350        3282 :         nExpirationBlocks = std::min(576, Params().GetConsensus().nSuperblockCycle);
     351        3282 :         break;
     352             :     default:
     353           0 :         nExpirationBlocks = std::min(24, Params().GetConsensus().nSuperblockCycle);
     354           0 :         break;
     355             :     }
     356             : 
     357        5228 :     int nExpirationBlock = nBlockHeight + nExpirationBlocks;
     358             : 
     359        5228 :     LogPrint(BCLog::GOBJECT, "CSuperblock::IsExpired -- nBlockHeight = %d, nExpirationBlock = %d\n", nBlockHeight, nExpirationBlock);
     360             : 
     361        5228 :     if (heightToTest > nExpirationBlock) {
     362         104 :         LogPrint(BCLog::GOBJECT, "CSuperblock::IsExpired -- Outdated trigger found\n");
     363         104 :         return true;
     364             :     }
     365             : 
     366        5124 :     if (Params().NetworkIDString() != CBaseChainParams::MAIN) {
     367             :         // NOTE: this can happen on testnet/devnets due to reorgs, should never happen on mainnet
     368        5124 :         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        5124 :     }
     373             : 
     374        5124 :     return false;
     375        5228 : }
     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         204 : 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         612 :     std::string str_addresses = Join(vecPayments, "|", [&](const auto& payment) {
     393         408 :         CTxDestination dest;
     394         408 :         ExtractDestination(payment.script, dest);
     395         408 :         return EncodeDestination(dest);
     396             :     });
     397         612 :     std::string str_amounts = Join(vecPayments, "|", [&](const auto& payment) {
     398         408 :         return ValueFromAmount(payment.nAmount).write();
     399           0 :     });
     400         612 :     std::string str_hashes = Join(vecPayments, "|", [&](const auto& payment) { return payment.proposalHash.ToString(); });
     401             : 
     402         204 :     std::stringstream ss;
     403         204 :     ss << "{";
     404         204 :     ss << "\"event_block_height\": " << nBlockHeight << ", ";
     405         204 :     ss << "\"payment_addresses\": \"" << str_addresses << "\", ";
     406         204 :     ss << "\"payment_amounts\": \"" << str_amounts << "\", ";
     407         204 :     ss << "\"proposal_hashes\": \"" << str_hashes << "\", ";
     408         204 :     ss << "\"type\":" << 2;
     409         204 :     ss << "}";
     410             : 
     411         204 :     return HexStr(ss.str());
     412         204 : }
     413             : 
     414        1908 : CGovernancePayment::CGovernancePayment(const CTxDestination& destIn, CAmount nAmountIn, const uint256& proposalHash) :
     415         954 :         fValid(false),
     416         954 :         script(),
     417         954 :         nAmount(0),
     418         954 :         proposalHash(proposalHash)
     419         954 : {
     420             :     try {
     421         954 :         script = GetScriptForDestination(destIn);
     422         954 :         nAmount = nAmountIn;
     423         954 :         fValid = true;
     424         954 :     } 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        1908 : }
     432             : 
     433             : namespace governance {
     434             : 
     435         188 : bool SuperblockManager::AddTrigger(std::shared_ptr<CGovernanceObject> obj, int cachedHeight)
     436             : {
     437         188 :     AssertLockNotHeld(cs_sb);
     438         188 :     if (!obj) return false;
     439             : 
     440         188 :     uint256 nHash = obj->GetHash();
     441             : 
     442         188 :     LOCK(cs_sb);
     443         188 :     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         188 :     CSuperblock_sptr pSuperblock;
     450             :     try {
     451         188 :         pSuperblock = std::make_shared<CSuperblock>(*obj, nHash);
     452         188 :     } 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         188 :     pSuperblock->SetStatus(SeenObjectStatus::Valid);
     461         188 :     m_triggers.emplace(nHash, TriggerEntry{pSuperblock, std::move(obj)});
     462             : 
     463         188 :     return !pSuperblock->IsExpired(cachedHeight);
     464         188 : }
     465             : 
     466          20 : void SuperblockManager::RemoveTrigger(const uint256& hash)
     467             : {
     468          20 :     LOCK(cs_sb);
     469          20 :     m_triggers.erase(hash);
     470          20 : }
     471             : 
     472       99040 : void SuperblockManager::Clean(int cachedHeight)
     473             : {
     474       99040 :     AssertLockNotHeld(cs_sb);
     475       99040 :     LOCK(cs_sb);
     476             : 
     477       99040 :     LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- m_triggers.size() = %d\n", __func__, m_triggers.size());
     478             : 
     479      104080 :     for (auto it = m_triggers.begin(); it != m_triggers.end();) {
     480        5040 :         bool remove = false;
     481       20368 :         const auto& [sb, obj] = it->second;
     482             : 
     483        5040 :         if (!sb) {
     484           0 :             LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- nullptr superblock\n", __func__);
     485           0 :             remove = true;
     486           0 :         } else {
     487        5040 :             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        5040 :             LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- superblock status = %d\n", __func__,
     492             :                      std23::to_underlying(sb->GetStatus()));
     493        5040 :             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        5040 :                 LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- Valid trigger found\n", __func__);
     502        5040 :                 if (sb->IsExpired(cachedHeight)) {
     503         104 :                     if (obj) obj->SetExpired();
     504         104 :                     remove = true;
     505         104 :                 }
     506        5040 :                 break;
     507             :             default:
     508           0 :                 break;
     509             :             }
     510             :         }
     511        5040 :         LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- %smarked for removal\n", __func__, remove ? "" : "NOT ");
     512             : 
     513        5040 :         if (remove) {
     514         104 :             std::string strDataAsPlainString = "nullptr";
     515         104 :             if (obj) {
     516         104 :                 strDataAsPlainString = obj->GetDataAsPlainString();
     517         104 :                 obj->PrepareDeletion(GetTime<std::chrono::seconds>().count());
     518         104 :             }
     519         104 :             LogPrint(BCLog::GOBJECT, "SuperblockManager::%s -- Removing trigger object %s\n", __func__,
     520             :                      strDataAsPlainString);
     521         104 :             it = m_triggers.erase(it);
     522         104 :         } else {
     523        4936 :             ++it;
     524             :         }
     525             :     }
     526       99040 : }
     527             : 
     528        3035 : void SuperblockManager::Clear()
     529             : {
     530        3035 :     LOCK(cs_sb);
     531        3035 :     m_triggers.clear();
     532        3035 :     m_loaded = false;
     533        3035 : }
     534             : 
     535       71623 : std::vector<CSuperblock_sptr> SuperblockManager::GetActiveTriggers() const
     536             : {
     537       71623 :     LOCK(cs_sb);
     538       71623 :     std::vector<CSuperblock_sptr> vecResults;
     539       71623 :     vecResults.reserve(m_triggers.size());
     540       75459 :     for (const auto& [_, entry] : m_triggers) {
     541        3836 :         if (entry.sb && entry.obj) {
     542        3836 :             vecResults.push_back(entry.sb);
     543        3836 :         }
     544             :     }
     545       71623 :     return vecResults;
     546       71623 : }
     547             : 
     548       81273 : bool SuperblockManager::GetBestSuperblock(const CDeterministicMNList& tip_mn_list, CSuperblock_sptr& sbRet,
     549             :                                           int nBlockHeight) const
     550             : {
     551       81273 :     LOCK(cs_sb);
     552       81273 :     return GetBestSuperblockInternal(tip_mn_list, sbRet, nBlockHeight);
     553       81273 : }
     554             : 
     555      287834 : bool SuperblockManager::GetBestSuperblockInternal(const CDeterministicMNList& tip_mn_list, CSuperblock_sptr& sbRet,
     556             :                                                   int nBlockHeight) const
     557             : {
     558      287834 :     AssertLockHeld(cs_sb);
     559      287834 :     if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) {
     560      285682 :         return false;
     561             :     }
     562             : 
     563        2152 :     int nYesCount = 0;
     564        3420 :     for (const auto& [_, entry] : m_triggers) {
     565        1268 :         if (!entry.sb || !entry.obj || nBlockHeight != entry.sb->GetBlockHeight()) {
     566         530 :             continue;
     567             :         }
     568         738 :         int nTempYesCount = entry.obj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING);
     569         738 :         if (nTempYesCount > nYesCount) {
     570         426 :             nYesCount = nTempYesCount;
     571         426 :             sbRet = entry.sb;
     572         426 :         }
     573             :     }
     574        2152 :     return nYesCount > 0;
     575      287834 : }
     576             : 
     577      107224 : bool SuperblockManager::IsSuperblockTriggered(const CDeterministicMNList& tip_mn_list, int nBlockHeight)
     578             : {
     579      107224 :     LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- Start nBlockHeight = %d\n", nBlockHeight);
     580      107224 :     if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) {
     581      105126 :         return false;
     582             :     }
     583             : 
     584        2098 :     LOCK(cs_sb);
     585             : 
     586        2098 :     LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- m_triggers.size() = %d\n", m_triggers.size());
     587             : 
     588        3767 :     for (const auto& [_, entry] : m_triggers) {
     589         479 :         if (!entry.sb) {
     590           0 :             LogPrintf("IsSuperblockTriggered -- Non-superblock found, continuing\n");
     591           0 :             continue;
     592             :         }
     593         479 :         if (!entry.obj) {
     594           0 :             LogPrintf("IsSuperblockTriggered -- pObj == nullptr, continuing\n");
     595           0 :             continue;
     596             :         }
     597             : 
     598         479 :         LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- data = %s\n", entry.obj->GetDataAsPlainString());
     599             : 
     600         958 :         if (nBlockHeight != entry.sb->GetBlockHeight()) {
     601         187 :             LogPrint(BCLog::GOBJECT, /* Continued */
     602             :                      "IsSuperblockTriggered -- block height doesn't match nBlockHeight = %d, blockStart = %d, "
     603             :                      "continuing\n",
     604             :                      nBlockHeight, entry.sb->GetBlockHeight());
     605         187 :             continue;
     606             :         }
     607             : 
     608         292 :         entry.obj->UpdateSentinelVariables(tip_mn_list);
     609             : 
     610         292 :         if (entry.obj->IsSetCachedFunding()) {
     611         247 :             LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- fCacheFunding = true, returning true\n");
     612         247 :             return true;
     613             :         } else {
     614          45 :             LogPrint(BCLog::GOBJECT, "IsSuperblockTriggered -- fCacheFunding = false, continuing\n");
     615             :         }
     616             :     }
     617             : 
     618        1851 :     return false;
     619      107224 : }
     620             : 
     621         230 : 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         230 :     LOCK(cs_sb);
     625         230 :     CSuperblock_sptr pSuperblock;
     626         230 :     if (GetBestSuperblockInternal(tip_mn_list, pSuperblock, nBlockHeight)) {
     627         230 :         return pSuperblock->IsValid(active_chain, txNew, nBlockHeight, blockReward, is_v24);
     628             :     }
     629           0 :     return false;
     630         230 : }
     631             : 
     632          17 : bool SuperblockManager::GetSuperblockPayments(const CDeterministicMNList& tip_mn_list, int nBlockHeight,
     633             :                                               std::vector<CTxOut>& voutSuperblockRet) const
     634             : {
     635          17 :     LOCK(cs_sb);
     636             : 
     637          17 :     CSuperblock_sptr pSuperblock;
     638          17 :     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          17 :     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          51 :     for (int i = 0; i < pSuperblock->CountPayments(); i++) {
     650          34 :         CGovernancePayment payment;
     651          34 :         if (pSuperblock->GetPayment(i, payment)) {
     652          34 :             voutSuperblockRet.emplace_back(payment.nAmount, payment.script);
     653             : 
     654          34 :             CTxDestination dest;
     655          34 :             ExtractDestination(payment.script, dest);
     656             : 
     657          34 :             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          34 :         } else {
     660           0 :             LogPrint(BCLog::GOBJECT, "GetSuperblockPayments -- Payment not found\n");
     661             :         }
     662          34 :     }
     663             : 
     664          17 :     return true;
     665          17 : }
     666             : 
     667      206314 : void SuperblockManager::ExecuteBestSuperblock(const CDeterministicMNList& tip_mn_list, int nBlockHeight)
     668             : {
     669      206314 :     LOCK(cs_sb);
     670      206314 :     CSuperblock_sptr pSuperblock;
     671      206314 :     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          98 :         pSuperblock->SetExecuted();
     675          98 :     }
     676      206314 : }
     677             : 
     678             : } // namespace governance

Generated by: LCOV version 1.16