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 : }
|