From 0c4d2df5e5d03de3c7c6650b8fb3323ded02f31c Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Wed, 26 Jul 2023 18:57:23 +0200 Subject: [PATCH 1/2] Enforce correct coinstake payment --- src/validation.cpp | 24 ++++++++++++++++++++++++ test/lint/lint-circular-dependencies.sh | 1 + 2 files changed, 25 insertions(+) diff --git a/src/validation.cpp b/src/validation.cpp index bc8e163cb5ece..5961d7f0afec1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -11,6 +11,7 @@ #include "validation.h" #include "addrman.h" +#include "amount.h" #include "blocksignature.h" #include "budget/budgetmanager.h" #include "chainparams.h" @@ -21,6 +22,7 @@ #include "consensus/tx_verify.h" #include "consensus/validation.h" #include "consensus/zerocoin_verify.h" +#include "evo/deterministicmns.h" #include "evo/evodb.h" #include "evo/specialtx_validation.h" #include "flatfile.h" @@ -1567,6 +1569,28 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd CAmount txValueOut = tx.GetValueOut(); if (!tx.IsCoinBase()) { CAmount txValueIn = view.GetValueIn(tx); + // Once v6 is enforced and legacy mns are obsolete check that CoinStake does not overmint + if (tx.IsCoinStake() && isV6UpgradeEnforced && deterministicMNManager->LegacyMNObsolete(pindex->nHeight)) { + CAmount stakeMint = txValueOut - txValueIn; + // This is a possible superblock: coinstake cannot pay more than the blockvalue (TODO: update for single superblock payment) + if (pindex->nHeight % consensus.nBudgetCycleBlocks < 100) { + CAmount maxStakeMint = GetBlockValue(pindex->nHeight); + if (stakeMint > maxStakeMint) { + return state.DoS(100, error("%s: coinstake pays too much (actual=%s vs limit=%s)", __func__, FormatMoney(stakeMint), FormatMoney(maxStakeMint)), + REJECT_INVALID, "bad-blk-stake-amount"); + } + } else { + // Masternode found, subtract its reward from the expected stake reward + CAmount nExpectedStakeMint = GetBlockValue(pindex->nHeight); + if (deterministicMNManager->GetListForBlock(pindex->pprev).GetMNPayee()) { + nExpectedStakeMint -= GetMasternodePayment(pindex->nHeight); + } + if (stakeMint != nExpectedStakeMint) { + return state.DoS(100, error("%s: coinstake pays too much (actual=%s vs limit=%s)", __func__, FormatMoney(stakeMint), FormatMoney(nExpectedStakeMint)), + REJECT_INVALID, "bad-blk-stake-amount"); + } + } + } if (!tx.IsCoinStake()) nFees += txValueIn - txValueOut; nValueIn += txValueIn; diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 7d984d21df94a..4d31b177c497f 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -55,6 +55,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "chain -> legacy/stakemodifier -> validation -> pow -> chain" "evo/deterministicmns -> masternodeman -> net -> tiertwo/net_masternodes -> evo/deterministicmns" "evo/deterministicmns -> masternodeman -> validation -> validationinterface -> evo/deterministicmns" + "evo/deterministicmns -> masternodeman -> validation -> evo/deterministicmns" ) EXIT_CODE=0 From 06cc5fc67b1b04dc47a3b025a27242c8173aa68c Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 28 Jul 2023 14:46:30 +0200 Subject: [PATCH 2/2] Check coinbase validity for DMNs --- src/masternode-payments.cpp | 32 +++++++++++++++++++++++--------- src/masternode-payments.h | 1 + src/validation.cpp | 9 +++------ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 9379a3a503a2a..9f85d4a79dfc8 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -5,15 +5,16 @@ #include "masternode-payments.h" +#include "budget/budgetmanager.h" #include "chainparams.h" #include "evo/deterministicmns.h" #include "fs.h" -#include "budget/budgetmanager.h" #include "masternodeman.h" #include "netmessagemaker.h" -#include "tiertwo/netfulfilledman.h" #include "spork.h" +#include "sporkid.h" #include "sync.h" +#include "tiertwo/netfulfilledman.h" #include "tiertwo/tiertwo_sync_state.h" #include "util/system.h" #include "utilmoneystr.h" @@ -228,16 +229,25 @@ bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev) { int nBlockHeight = pindexPrev->nHeight + 1; TrxValidationStatus transactionStatus = TrxValidationStatus::InValid; + const bool isV6UpgradeEnforced = Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0); + const bool isLegacyObsolete = deterministicMNManager->LegacyMNObsolete(nBlockHeight); + + const bool fPayCoinstake = Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_POS) && + !isV6UpgradeEnforced; + const CTransaction& txNew = *(fPayCoinstake ? block.vtx[1] : block.vtx[0]); + + // If v6 is enforced and legacy mns are obsolete even not-synced nodes can check dmns reward + if (!g_tiertwo_sync_state.IsSynced() && isV6UpgradeEnforced && isLegacyObsolete) { + // This is a possible superblock cannot check anything: (TODO: update for single superblock payment) + if (nBlockHeight % Params().GetConsensus().nBudgetCycleBlocks < 100) return true; + return CheckMasternodePayee(txNew, pindexPrev); + } if (!g_tiertwo_sync_state.IsSynced()) { //there is no budget data to use to check anything -- find the longest chain LogPrint(BCLog::MASTERNODE, "Client not synced, skipping block payee checks\n"); return true; } - const bool fPayCoinstake = Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_POS) && - !Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0); - const CTransaction& txNew = *(fPayCoinstake ? block.vtx[1] : block.vtx[0]); - //check if it's a budget block if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) { if (g_budgetman.IsBudgetPaymentBlock(nBlockHeight)) { @@ -262,17 +272,21 @@ bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev) // In all cases a masternode will get the payment for this block //check for masternode payee + return CheckMasternodePayee(txNew, pindexPrev); +} + +bool CheckMasternodePayee(const CTransaction& txNew, const CBlockIndex* pindexPrev) +{ if (masternodePayments.IsTransactionValid(txNew, pindexPrev)) return true; - LogPrint(BCLog::MASTERNODE,"Invalid mn payment detected %s\n", txNew.ToString().c_str()); + LogPrint(BCLog::MASTERNODE, "Invalid mn payment detected %s\n", txNew.ToString().c_str()); if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) return false; - LogPrint(BCLog::MASTERNODE,"Masternode payment enforcement is disabled, accepting block\n"); + LogPrint(BCLog::MASTERNODE, "Masternode payment enforcement is disabled, accepting block\n"); return true; } - void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake) { if (!sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) || // if superblocks are not enabled diff --git a/src/masternode-payments.h b/src/masternode-payments.h index 17be8863b7152..68d4c5da370a3 100644 --- a/src/masternode-payments.h +++ b/src/masternode-payments.h @@ -26,6 +26,7 @@ extern CMasternodePayments masternodePayments; #define MNPAYMENTS_SIGNATURES_TOTAL 10 bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev); +bool CheckMasternodePayee(const CTransaction& txNew, const CBlockIndex* pindexPrev); std::string GetRequiredPaymentsString(int nBlockHeight); bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CAmount& nBudgetAmt); void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake); diff --git a/src/validation.cpp b/src/validation.cpp index 5961d7f0afec1..1d9a2eeb896be 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1661,12 +1661,9 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } // Masternode/Budget payments - // !TODO: after transition to DMN is complete, check this also during IBD - if (!fInitialBlockDownload) { - if (!IsBlockPayeeValid(block, pindex->pprev)) { - mapRejectedBlocks.emplace(block.GetHash(), GetTime()); - return state.DoS(0, false, REJECT_INVALID, "bad-cb-payee", false, "Couldn't find masternode/budget payment"); - } + if (!IsBlockPayeeValid(block, pindex->pprev)) { + mapRejectedBlocks.emplace(block.GetHash(), GetTime()); + return state.DoS(0, false, REJECT_INVALID, "bad-cb-payee", false, "Couldn't find masternode/budget payment"); } // After v6 enforcement: Check that the coinbase pays the exact amount