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 44536 : CAmount PlatformShare(const CAmount reward)
26 : {
27 44536 : const CAmount platformReward = reward * 375 / 1000;
28 44536 : bool ok = MoneyRange(platformReward);
29 44536 : assert(ok);
30 44536 : return platformReward;
31 : }
32 :
33 40513 : [[nodiscard]] bool CMNPaymentsProcessor::GetBlockTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
34 : std::vector<CTxOut>& voutMasternodePaymentsRet)
35 : {
36 40513 : voutMasternodePaymentsRet.clear();
37 :
38 40513 : const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
39 :
40 40513 : bool fV20Active = DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_V20);
41 40513 : 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 40513 : if (fV20Active && DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_MN_RR)) {
46 18213 : CAmount masternodeSubsidyReward = GetMasternodePayment(nBlockHeight, blockSubsidy, fV20Active);
47 18213 : const CAmount platformReward = PlatformShare(masternodeSubsidyReward);
48 18213 : masternodeReward -= platformReward;
49 :
50 18213 : assert(MoneyRange(masternodeReward));
51 :
52 18213 : LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- MN reward %lld reallocated to credit pool\n", __func__, platformReward);
53 18213 : voutMasternodePaymentsRet.emplace_back(platformReward, CScript() << OP_RETURN);
54 18213 : }
55 40513 : const auto mnList = m_dmnman.GetListForBlock(pindexPrev);
56 40513 : if (mnList.GetCounts().total() == 0) {
57 34358 : LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- no masternode registered to receive a payment\n", __func__);
58 34358 : return true;
59 : }
60 6155 : const auto dmnPayee = mnList.GetMNPayee(pindexPrev);
61 6155 : if (!dmnPayee) {
62 1 : return false;
63 : }
64 :
65 6154 : CAmount operatorReward = 0;
66 :
67 6154 : 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 0 : operatorReward = (masternodeReward * dmnPayee->nOperatorReward) / 10000;
71 0 : masternodeReward -= operatorReward;
72 0 : }
73 :
74 6154 : if (masternodeReward > 0) {
75 6154 : voutMasternodePaymentsRet.emplace_back(masternodeReward, dmnPayee->pdmnState->scriptPayout);
76 6154 : }
77 6154 : if (operatorReward > 0) {
78 0 : voutMasternodePaymentsRet.emplace_back(operatorReward, dmnPayee->pdmnState->scriptOperatorPayout);
79 0 : }
80 :
81 6154 : return true;
82 40513 : }
83 :
84 : /**
85 : * GetMasternodeTxOuts
86 : *
87 : * Get masternode payment tx outputs
88 : */
89 24517 : [[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 24517 : voutMasternodePaymentsRet.clear();
94 :
95 24517 : if(!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePaymentsRet)) {
96 1 : LogPrintf("CMNPaymentsProcessor::%s -- ERROR Failed to get payee\n", __func__);
97 1 : return false;
98 : }
99 :
100 33176 : for (const auto& txout : voutMasternodePaymentsRet) {
101 8660 : CTxDestination dest;
102 8660 : ExtractDestination(txout.scriptPubKey, dest);
103 :
104 8660 : LogPrintf("CMNPaymentsProcessor::%s -- Masternode payment %lld to %s\n", __func__, txout.nValue, EncodeDestination(dest));
105 : }
106 :
107 24516 : return true;
108 24517 : }
109 :
110 49009 : [[nodiscard]] bool CMNPaymentsProcessor::IsTransactionValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy,
111 : const CAmount feeReward)
112 : {
113 49009 : const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
114 49009 : if (!DeploymentDIP0003Enforced(nBlockHeight, m_consensus_params)) {
115 : // can't verify historical blocks here
116 33013 : return true;
117 : }
118 :
119 15996 : std::vector<CTxOut> voutMasternodePayments;
120 15996 : if (!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePayments)) {
121 0 : LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Failed to get payees for block at height %s\n", __func__, nBlockHeight);
122 0 : return true;
123 : }
124 :
125 31703 : for (const auto& txout : voutMasternodePayments) {
126 47730 : bool found = std::ranges::any_of(txNew.vout, [&txout](const auto& txout2) { return txout == txout2; });
127 15707 : if (!found) {
128 0 : std::string str_payout;
129 0 : if (CTxDestination dest; ExtractDestination(txout.scriptPubKey, dest)) {
130 0 : str_payout = "address=" + EncodeDestination(dest);
131 0 : } else {
132 0 : str_payout = "scriptPubKey=" + HexStr(txout.scriptPubKey);
133 : }
134 0 : LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Failed to find expected payee %s amount=%lld height=%d\n",
135 : __func__, str_payout, txout.nValue, nBlockHeight);
136 0 : return false;
137 0 : }
138 : }
139 15996 : return true;
140 49009 : }
141 :
142 4026 : [[nodiscard]] bool CMNPaymentsProcessor::IsOldBudgetBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet)
143 : {
144 4026 : bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward);
145 :
146 4026 : 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 4026 : 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 4026 : int nOffset = nBlockHeight % m_consensus_params.nBudgetPaymentsCycleBlocks;
160 4026 : 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 806 : 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 806 : return isBlockRewardValueMet;
169 : }
170 3220 : 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 3220 : return isBlockRewardValueMet;
175 4026 : }
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 49009 : bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const CBlockIndex* pindexPrev, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock)
188 : {
189 49009 : const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
190 49009 : bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward);
191 :
192 49009 : strErrorRet = "";
193 :
194 49009 : 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 43220 : if(!isBlockRewardValueMet) {
197 0 : strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, old budgets are not activated yet",
198 0 : nBlockHeight, block.vtx[0]->GetValueOut(), blockReward);
199 0 : }
200 43220 : return isBlockRewardValueMet;
201 5789 : } else if (nBlockHeight < m_consensus_params.nSuperblockStartBlock) {
202 : // superblocks are not enabled yet, check if we can pass old budget rules
203 4026 : return IsOldBudgetBlockValueValid(block, nBlockHeight, blockReward, strErrorRet);
204 : }
205 :
206 1763 : LogPrint(BCLog::MNPAYMENTS, "block.vtx[0]->GetValueOut() %lld <= blockReward %lld\n", block.vtx[0]->GetValueOut(), blockReward);
207 :
208 1763 : CAmount nSuperblockMaxValue = blockReward + CSuperblock::GetPaymentsLimit(m_chainman.ActiveChain(), nBlockHeight);
209 1763 : bool isSuperblockMaxValueMet = (block.vtx[0]->GetValueOut() <= nSuperblockMaxValue);
210 :
211 1763 : LogPrint(BCLog::GOBJECT, "block.vtx[0]->GetValueOut() %lld <= nSuperblockMaxValue %lld\n", block.vtx[0]->GetValueOut(), nSuperblockMaxValue);
212 :
213 1763 : if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) {
214 : // can't possibly be a superblock, so lets just check for block reward limits
215 1677 : 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 1677 : return isBlockRewardValueMet;
220 : }
221 :
222 : // bail out in case superblock limits were exceeded
223 86 : 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 86 : 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 0 : if (!check_superblock) return true;
237 :
238 : // we are synced and possibly on a superblock now
239 :
240 0 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
241 :
242 0 : 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 0 : 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 0 : return isBlockRewardValueMet;
250 : }
251 :
252 : // this actually also checks for correct payees and not only amount
253 0 : const bool is_v24{DeploymentActiveAfter(pindexPrev, m_chainman, Consensus::DEPLOYMENT_V24)};
254 0 : 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 0 : return true;
264 49009 : }
265 :
266 49009 : bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, const bool check_superblock)
267 : {
268 49009 : const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
269 :
270 : // Check for correct masternode payment
271 49009 : if (IsTransactionValid(txNew, pindexPrev, blockSubsidy, feeReward)) {
272 49009 : LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- Valid masternode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
273 49009 : } else {
274 0 : LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid masternode payment detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
275 0 : return false;
276 : }
277 :
278 49009 : if (!m_superblocks.IsValid()) {
279 : // governance data is either incomplete or non-existent
280 49009 : LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- WARNING! Not enough data, skipping superblock payee checks\n", __func__);
281 49009 : return true; // not an error
282 : }
283 :
284 0 : 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 0 : LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- WARNING! Client synced but old budget system is disabled, accepting any payee\n", __func__);
291 0 : return true; // not an error
292 : }
293 :
294 : // superblocks started
295 0 : if (!check_superblock) return true;
296 :
297 0 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
298 0 : const bool is_v24{DeploymentActiveAfter(pindexPrev, m_chainman, Consensus::DEPLOYMENT_V24)};
299 0 : if (m_superblocks.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) {
300 0 : if (m_superblocks.IsValidSuperblock(m_chainman.ActiveChain(), tip_mn_list, txNew, nBlockHeight,
301 0 : blockSubsidy + feeReward, is_v24)) {
302 0 : 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 0 : } 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 0 : } else {
311 0 : LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- No triggered superblock detected at height %d\n",
312 : __func__, nBlockHeight);
313 : }
314 :
315 0 : return true;
316 49009 : }
317 :
318 24517 : 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 24517 : int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
322 :
323 : // Only create superblocks when one is actually triggered.
324 24517 : const auto tip_mn_list = m_dmnman.GetListAtChainTip();
325 24517 : if (m_superblocks.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) {
326 0 : LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Triggered superblock creation at height %d\n", __func__, nBlockHeight);
327 0 : m_superblocks.GetSuperblockPayments(tip_mn_list, nBlockHeight, voutSuperblockPaymentsRet);
328 0 : }
329 :
330 24517 : if (!GetMasternodeTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePaymentsRet)) {
331 1 : LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- No masternode to pay (MN list probably empty)\n", __func__);
332 1 : }
333 :
334 24517 : txNew.vout.insert(txNew.vout.end(), voutMasternodePaymentsRet.begin(), voutMasternodePaymentsRet.end());
335 24517 : txNew.vout.insert(txNew.vout.end(), voutSuperblockPaymentsRet.begin(), voutSuperblockPaymentsRet.end());
336 :
337 24517 : std::string voutMasternodeStr;
338 33177 : for (const auto& txout : voutMasternodePaymentsRet) {
339 : // subtract MN payment from miner reward
340 8660 : txNew.vout[0].nValue -= txout.nValue;
341 8660 : if (!voutMasternodeStr.empty())
342 315 : voutMasternodeStr += ",";
343 8660 : voutMasternodeStr += txout.ToString();
344 : }
345 :
346 24517 : LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- nBlockHeight %d blockReward %lld voutMasternodePaymentsRet \"%s\" txNew %s", __func__, /* Continued */
347 : nBlockHeight, blockSubsidy + feeReward, voutMasternodeStr, txNew.ToString());
348 24517 : }
|