LCOV - code coverage report
Current view: top level - src/masternode - payments.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 157 182 86.3 %
Date: 2026-06-25 07:23:43 Functions: 9 9 100.0 %

          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 <masternode/payments.h>
       6             : 
       7             : #include <evo/deterministicmns.h>
       8             : #include <governance/superblock.h>
       9             : #include <masternode/sync.h>
      10             : 
      11             : #include <chain.h>
      12             : #include <consensus/amount.h>
      13             : #include <deploymentstatus.h>
      14             : #include <key_io.h>
      15             : #include <logging.h>
      16             : #include <primitives/block.h>
      17             : #include <script/standard.h>
      18             : #include <tinyformat.h>
      19             : #include <validation.h>
      20             : 
      21             : #include <cassert>
      22             : #include <ranges>
      23             : #include <string>
      24             : 
      25      256758 : CAmount PlatformShare(const CAmount reward)
      26             : {
      27      256758 :     const CAmount platformReward = reward * 375 / 1000;
      28      256758 :     bool ok = MoneyRange(platformReward);
      29      256758 :     assert(ok);
      30      256758 :     return platformReward;
      31             : }
      32             : 
      33      250397 : [[nodiscard]] bool CMNPaymentsProcessor::GetBlockTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
      34             :                                                         std::vector<CTxOut>& voutMasternodePaymentsRet)
      35             : {
      36      250397 :     voutMasternodePaymentsRet.clear();
      37             : 
      38      250397 :     const int nBlockHeight = pindexPrev  == nullptr ? 0 : pindexPrev->nHeight + 1;
      39             : 
      40      250397 :     bool fV20Active = DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_V20);
      41      250397 :     CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockSubsidy + feeReward, fV20Active);
      42             : 
      43             :     // Credit Pool doesn't exist before V20. If any part of reward will re-allocated to credit pool before v20
      44             :     // activation these fund will be just permanently lost. Applicable for devnets, regtest, testnet
      45      250397 :     if (fV20Active && DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_MN_RR)) {
      46      116318 :         CAmount masternodeSubsidyReward = GetMasternodePayment(nBlockHeight, blockSubsidy, fV20Active);
      47      116318 :         const CAmount platformReward = PlatformShare(masternodeSubsidyReward);
      48      116318 :         masternodeReward -= platformReward;
      49             : 
      50      116318 :         assert(MoneyRange(masternodeReward));
      51             : 
      52      116318 :         LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- MN reward %lld reallocated to credit pool\n", __func__, platformReward);
      53      116318 :         voutMasternodePaymentsRet.emplace_back(platformReward, CScript() << OP_RETURN);
      54      116318 :     }
      55      250397 :     const auto mnList = m_dmnman.GetListForBlock(pindexPrev);
      56      250397 :     if (mnList.GetCounts().total() == 0) {
      57      117108 :         LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- no masternode registered to receive a payment\n", __func__);
      58      117108 :         return true;
      59             :     }
      60      133289 :     const auto dmnPayee = mnList.GetMNPayee(pindexPrev);
      61      133289 :     if (!dmnPayee) {
      62         571 :         return false;
      63             :     }
      64             : 
      65      132718 :     CAmount operatorReward = 0;
      66             : 
      67      236195 :     if (dmnPayee->nOperatorReward != 0 && dmnPayee->pdmnState->scriptOperatorPayout != CScript()) {
      68             :         // This calculation might eventually turn out to result in 0 even if an operator reward percentage is given.
      69             :         // This will however only happen in a few years when the block rewards drops very low.
      70       77019 :         operatorReward = (masternodeReward * dmnPayee->nOperatorReward) / 10000;
      71       77019 :         masternodeReward -= operatorReward;
      72       77019 :     }
      73             : 
      74      132718 :     if (masternodeReward > 0) {
      75      132718 :         voutMasternodePaymentsRet.emplace_back(masternodeReward, dmnPayee->pdmnState->scriptPayout);
      76      132718 :     }
      77      132718 :     if (operatorReward > 0) {
      78       77019 :         voutMasternodePaymentsRet.emplace_back(operatorReward, dmnPayee->pdmnState->scriptOperatorPayout);
      79       77019 :     }
      80             : 
      81      132718 :     return true;
      82      250397 : }
      83             : 
      84             : /**
      85             : *   GetMasternodeTxOuts
      86             : *
      87             : *   Get masternode payment tx outputs
      88             : */
      89       87814 : [[nodiscard]] bool CMNPaymentsProcessor::GetMasternodeTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
      90             :                                                              std::vector<CTxOut>& voutMasternodePaymentsRet)
      91             : {
      92             :     // make sure it's not filled yet
      93       87814 :     voutMasternodePaymentsRet.clear();
      94             : 
      95       87814 :     if(!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePaymentsRet)) {
      96         115 :         LogPrintf("CMNPaymentsProcessor::%s -- ERROR Failed to get payee\n", __func__);
      97         115 :         return false;
      98             :     }
      99             : 
     100      135421 :     for (const auto& txout : voutMasternodePaymentsRet) {
     101       47722 :         CTxDestination dest;
     102       47722 :         ExtractDestination(txout.scriptPubKey, dest);
     103             : 
     104       47722 :         LogPrintf("CMNPaymentsProcessor::%s -- Masternode payment %lld to %s\n", __func__, txout.nValue, EncodeDestination(dest));
     105             :     }
     106             : 
     107       87699 :     return true;
     108       87814 : }
     109             : 
     110      340850 : [[nodiscard]] bool CMNPaymentsProcessor::IsTransactionValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy,
     111             :                                                             const CAmount feeReward)
     112             : {
     113      340850 :     const int nBlockHeight = pindexPrev  == nullptr ? 0 : pindexPrev->nHeight + 1;
     114      340850 :     if (!DeploymentDIP0003Enforced(nBlockHeight, m_consensus_params)) {
     115             :         // can't verify historical blocks here
     116      178267 :         return true;
     117             :     }
     118             : 
     119      162583 :     std::vector<CTxOut> voutMasternodePayments;
     120      162583 :     if (!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePayments)) {
     121         456 :         LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Failed to get payees for block at height %s\n", __func__, nBlockHeight);
     122         456 :         return true;
     123             :     }
     124             : 
     125      440204 :     for (const auto& txout : voutMasternodePayments) {
     126     1020474 :         bool found = std::ranges::any_of(txNew.vout, [&txout](const auto& txout2) { return txout == txout2; });
     127      278333 :         if (!found) {
     128         256 :             std::string str_payout;
     129         256 :             if (CTxDestination dest; ExtractDestination(txout.scriptPubKey, dest)) {
     130         256 :                 str_payout = "address=" + EncodeDestination(dest);
     131         256 :             } else {
     132           0 :                 str_payout = "scriptPubKey=" + HexStr(txout.scriptPubKey);
     133             :             }
     134         256 :             LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Failed to find expected payee %s amount=%lld height=%d\n",
     135             :                       __func__, str_payout, txout.nValue, nBlockHeight);
     136         256 :             return false;
     137         256 :         }
     138             :     }
     139      161871 :     return true;
     140      340850 : }
     141             : 
     142        9150 : [[nodiscard]] bool CMNPaymentsProcessor::IsOldBudgetBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet)
     143             : {
     144        9150 :     bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward);
     145             : 
     146        9150 :     if (nBlockHeight < m_consensus_params.nBudgetPaymentsStartBlock) {
     147           0 :         strErrorRet = strprintf("Incorrect block %d, old budgets are not activated yet", nBlockHeight);
     148           0 :         return false;
     149             :     }
     150             : 
     151        9150 :     if (nBlockHeight >= m_consensus_params.nSuperblockStartBlock) {
     152           0 :         strErrorRet = strprintf("Incorrect block %d, old budgets are no longer active", nBlockHeight);
     153           0 :         return false;
     154             :     }
     155             : 
     156             :     // we are still using budgets, but we have no data about them anymore,
     157             :     // all we know is predefined budget cycle and window
     158             : 
     159        9150 :     int nOffset = nBlockHeight % m_consensus_params.nBudgetPaymentsCycleBlocks;
     160        9150 :     if (nOffset < m_consensus_params.nBudgetPaymentsWindowBlocks) {
     161             :         // NOTE: old budget system is disabled since 12.1
     162             :         // no old budget blocks should be accepted here on mainnet,
     163             :         // testnet/devnet/regtest should produce regular blocks only
     164        1900 :         if(!isBlockRewardValueMet) {
     165           0 :             strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, old budgets are disabled",
     166           0 :                                     nBlockHeight, block.vtx[0]->GetValueOut(), blockReward);
     167           0 :         }
     168        1900 :         return isBlockRewardValueMet;
     169             :     }
     170        7250 :     if(!isBlockRewardValueMet) {
     171           0 :         strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, block is not in old budget cycle window",
     172           0 :                                 nBlockHeight, block.vtx[0]->GetValueOut(), blockReward);
     173           0 :     }
     174        7250 :     return isBlockRewardValueMet;
     175        9150 : }
     176             : 
     177             : /**
     178             : * IsBlockValueValid
     179             : *
     180             : *   Determine if coinbase outgoing created money is the correct value
     181             : *
     182             : *   Why is this needed?
     183             : *   - In Dash some blocks are superblocks, which output much higher amounts of coins
     184             : *   - Other blocks are 10% lower in outgoing value, so in total, no extra coins are created
     185             : *   - When non-superblocks are detected, the normal schedule should be maintained
     186             : */
     187      340870 : bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const CBlockIndex* pindexPrev, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock)
     188             : {
     189      340870 :     const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
     190      340870 :     bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward);
     191             : 
     192      340870 :     strErrorRet = "";
     193             : 
     194      340870 :     if (nBlockHeight < m_consensus_params.nBudgetPaymentsStartBlock) {
     195             :         // old budget system is not activated yet, just make sure we do not exceed the regular block reward
     196      304570 :         if(!isBlockRewardValueMet) {
     197          20 :             strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, old budgets are not activated yet",
     198          20 :                                     nBlockHeight, block.vtx[0]->GetValueOut(), blockReward);
     199          20 :         }
     200      304570 :         return isBlockRewardValueMet;
     201       36300 :     } else if (nBlockHeight < m_consensus_params.nSuperblockStartBlock) {
     202             :         // superblocks are not enabled yet, check if we can pass old budget rules
     203        9150 :         return IsOldBudgetBlockValueValid(block, nBlockHeight, blockReward, strErrorRet);
     204             :     }
     205             : 
     206       27150 :     LogPrint(BCLog::MNPAYMENTS, "block.vtx[0]->GetValueOut() %lld <= blockReward %lld\n", block.vtx[0]->GetValueOut(), blockReward);
     207             : 
     208       27150 :     CAmount nSuperblockMaxValue =  blockReward + CSuperblock::GetPaymentsLimit(m_chainman.ActiveChain(), nBlockHeight);
     209       27150 :     bool isSuperblockMaxValueMet = (block.vtx[0]->GetValueOut() <= nSuperblockMaxValue);
     210             : 
     211       27150 :     LogPrint(BCLog::GOBJECT, "block.vtx[0]->GetValueOut() %lld <= nSuperblockMaxValue %lld\n", block.vtx[0]->GetValueOut(), nSuperblockMaxValue);
     212             : 
     213       27150 :     if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) {
     214             :         // can't possibly be a superblock, so lets just check for block reward limits
     215       25785 :         if (!isBlockRewardValueMet) {
     216           0 :             strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, only regular blocks are allowed at this height",
     217           0 :                                     nBlockHeight, block.vtx[0]->GetValueOut(), blockReward);
     218           0 :         }
     219       25785 :         return isBlockRewardValueMet;
     220             :     }
     221             : 
     222             :     // bail out in case superblock limits were exceeded
     223        1365 :     if (!isSuperblockMaxValueMet) {
     224           0 :         strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded superblock max value",
     225           0 :                                 nBlockHeight, block.vtx[0]->GetValueOut(), nSuperblockMaxValue);
     226           0 :         return false;
     227             :     }
     228             : 
     229        1365 :     if (!m_superblocks.IsValid()) {
     230          86 :         LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- WARNING! Not enough data, checked superblock max bounds only\n", __func__);
     231             :         // not enough data for full checks but at least we know that the superblock limits were honored.
     232             :         // We rely on the network to have followed the correct chain in this case
     233          86 :         return true;
     234             :     }
     235             : 
     236        1279 :     if (!check_superblock) return true;
     237             : 
     238             :     // we are synced and possibly on a superblock now
     239             : 
     240         931 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     241             : 
     242         931 :     if (!m_superblocks.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) {
     243             :         // we are on a valid superblock height but a superblock was not triggered
     244             :         // revert to block reward limits in this case
     245         816 :         if(!isBlockRewardValueMet) {
     246           0 :             strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, no triggered superblock detected",
     247           0 :                                     nBlockHeight, block.vtx[0]->GetValueOut(), blockReward);
     248           0 :         }
     249         816 :         return isBlockRewardValueMet;
     250             :     }
     251             : 
     252             :     // this actually also checks for correct payees and not only amount
     253         115 :     const bool is_v24{DeploymentActiveAfter(pindexPrev, m_chainman, Consensus::DEPLOYMENT_V24)};
     254         115 :     if (!m_superblocks.IsValidSuperblock(m_chainman.ActiveChain(), tip_mn_list, *block.vtx[0], nBlockHeight, blockReward, is_v24)) {
     255             :         // triggered but invalid? that's weird
     256           0 :         LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid superblock detected at height %d: %s", __func__, nBlockHeight, block.vtx[0]->ToString()); /* Continued */
     257             :         // should NOT allow invalid superblocks, when superblocks are enabled
     258           0 :         strErrorRet = strprintf("invalid superblock detected at height %d", nBlockHeight);
     259           0 :         return false;
     260             :     }
     261             : 
     262             :     // we got a valid superblock
     263         115 :     return true;
     264      340870 : }
     265             : 
     266      340850 : bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, const bool check_superblock)
     267             : {
     268      340850 :     const int nBlockHeight = pindexPrev  == nullptr ? 0 : pindexPrev->nHeight + 1;
     269             : 
     270             :     // Check for correct masternode payment
     271      340850 :     if (IsTransactionValid(txNew, pindexPrev, blockSubsidy, feeReward)) {
     272      340594 :         LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- Valid masternode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
     273      340594 :     } else {
     274         256 :         LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid masternode payment detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
     275         256 :         return false;
     276             :     }
     277             : 
     278      340594 :     if (!m_superblocks.IsValid()) {
     279             :         // governance data is either incomplete or non-existent
     280       51117 :         LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- WARNING! Not enough data, skipping superblock payee checks\n", __func__);
     281       51117 :         return true;  // not an error
     282             :     }
     283             : 
     284      289477 :     if (nBlockHeight < m_consensus_params.nSuperblockStartBlock) {
     285             :         // We are still using budgets, but we have no data about them anymore,
     286             :         // we can only check masternode payments.
     287             :         // NOTE: old budget system is disabled since 12.1 and we should never enter this branch
     288             :         // anymore when sync is finished (on mainnet). We have no old budget data but these blocks
     289             :         // have tons of confirmations and can be safely accepted without payee verification
     290      264346 :         LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- WARNING! Client synced but old budget system is disabled, accepting any payee\n", __func__);
     291      264346 :         return true; // not an error
     292             :     }
     293             : 
     294             :     // superblocks started
     295       25131 :     if (!check_superblock) return true;
     296             : 
     297       18357 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     298       18357 :     const bool is_v24{DeploymentActiveAfter(pindexPrev, m_chainman, Consensus::DEPLOYMENT_V24)};
     299       18357 :     if (m_superblocks.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) {
     300         115 :         if (m_superblocks.IsValidSuperblock(m_chainman.ActiveChain(), tip_mn_list, txNew, nBlockHeight,
     301         115 :                                             blockSubsidy + feeReward, is_v24)) {
     302         115 :             LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Valid superblock at height %d: %s", /* Continued */
     303             :                      __func__, nBlockHeight, txNew.ToString());
     304             :             // continue validation, should also pay MN
     305         115 :         } else {
     306           0 :             LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid superblock detected at height %d: %s", /* Continued */
     307             :                       __func__, nBlockHeight, txNew.ToString());
     308           0 :             return false;
     309             :         }
     310         115 :     } else {
     311       18242 :         LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- No triggered superblock detected at height %d\n",
     312             :                  __func__, nBlockHeight);
     313             :     }
     314             : 
     315       18357 :     return true;
     316      340850 : }
     317             : 
     318       87814 : void CMNPaymentsProcessor::FillBlockPayments(CMutableTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
     319             :                                              std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet)
     320             : {
     321       87814 :     int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
     322             : 
     323             :     // Only create superblocks when one is actually triggered.
     324       87814 :     const auto tip_mn_list = m_dmnman.GetListAtChainTip();
     325       87814 :     if (m_superblocks.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) {
     326          17 :         LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Triggered superblock creation at height %d\n", __func__, nBlockHeight);
     327          17 :         m_superblocks.GetSuperblockPayments(tip_mn_list, nBlockHeight, voutSuperblockPaymentsRet);
     328          17 :     }
     329             : 
     330       87814 :     if (!GetMasternodeTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePaymentsRet)) {
     331         115 :         LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- No masternode to pay (MN list probably empty)\n", __func__);
     332         115 :     }
     333             : 
     334       87814 :     txNew.vout.insert(txNew.vout.end(), voutMasternodePaymentsRet.begin(), voutMasternodePaymentsRet.end());
     335       87814 :     txNew.vout.insert(txNew.vout.end(), voutSuperblockPaymentsRet.begin(), voutSuperblockPaymentsRet.end());
     336             : 
     337       87814 :     std::string voutMasternodeStr;
     338      135536 :     for (const auto& txout : voutMasternodePaymentsRet) {
     339             :         // subtract MN payment from miner reward
     340       47722 :         txNew.vout[0].nValue -= txout.nValue;
     341       47722 :         if (!voutMasternodeStr.empty())
     342       18599 :             voutMasternodeStr += ",";
     343       47722 :         voutMasternodeStr += txout.ToString();
     344             :     }
     345             : 
     346       87814 :     LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- nBlockHeight %d blockReward %lld voutMasternodePaymentsRet \"%s\" txNew %s", __func__, /* Continued */
     347             :                             nBlockHeight, blockSubsidy + feeReward, voutMasternodeStr, txNew.ToString());
     348       87814 : }

Generated by: LCOV version 1.16