Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/evo/chainhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmn
m_chainlocks{chainlocks},
ehf_manager{std::make_unique<CMNHFManager>(evodb, chainman, qman)},
superblocks{std::make_unique<governance::SuperblockManager>()},
mn_payments{std::make_unique<CMNPaymentsProcessor>(dmnman, *superblocks, chainman, consensus_params)},
mn_payments{std::make_unique<CMNPaymentsProcessor>(dmnman, *superblocks, consensus_params)},
special_tx{std::make_unique<CSpecialTxProcessor>(*credit_pool_manager, dmnman, *ehf_manager, qblockman, qsnapman,
chainman, consensus_params, chainlocks, qman)}
{}
Expand Down
6 changes: 2 additions & 4 deletions src/evo/creditpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <logging.h>
#include <masternode/payments.h>
#include <node/blockstorage.h>
#include <validation.h>

Expand All @@ -24,9 +25,6 @@

using node::ReadBlockFromDisk;

// Forward declaration to prevent a new circular dependencies through masternode/payments.h
CAmount PlatformShare(const CAmount masternodeReward);

static const std::string DB_CREDITPOOL_SNAPSHOT = "cpm_S";

static bool GetDataFromUnlockTx(const CTransaction& tx, CAmount& toUnlock, uint64_t& index, TxValidationState& state)
Expand Down Expand Up @@ -253,7 +251,7 @@ CCreditPoolDiff::CCreditPoolDiff(CCreditPool starter, const CBlockIndex* pindexP

if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_MN_RR)) {
// If credit pool exists, it means v20 is activated
platformReward = PlatformShare(GetMasternodePayment(pindexPrev->nHeight + 1, blockSubsidy, /*fV20Active=*/ true));
platformReward = PlatformShare(GetMasternodePayment(pindexPrev->nHeight + 1, blockSubsidy, consensusParams, MnRewardEra::EvoReward));
}
}

Expand Down
103 changes: 85 additions & 18 deletions src/masternode/payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include <primitives/block.h>
#include <script/standard.h>
#include <tinyformat.h>
#include <validation.h>

#include <cassert>
#include <ranges>
Expand All @@ -30,20 +29,88 @@ CAmount PlatformShare(const CAmount reward)
return platformReward;
}

CAmount GetMasternodePayment(int nHeight, CAmount blockValue, const Consensus::Params& consensus_params, MnRewardEra era)
{
CAmount ret = blockValue/5; // start at 20%

const int nMNPIBlock = consensus_params.nMasternodePaymentsIncreaseBlock;
const int nMNPIPeriod = consensus_params.nMasternodePaymentsIncreasePeriod;
const int nReallocActivationHeight = consensus_params.BRRHeight;

// mainnet:
if(nHeight > nMNPIBlock) ret += blockValue / 20; // 158000 - 25.0% - 2014-10-24
if(nHeight > nMNPIBlock+(nMNPIPeriod* 1)) ret += blockValue / 20; // 175280 - 30.0% - 2014-11-25
if(nHeight > nMNPIBlock+(nMNPIPeriod* 2)) ret += blockValue / 20; // 192560 - 35.0% - 2014-12-26
if(nHeight > nMNPIBlock+(nMNPIPeriod* 3)) ret += blockValue / 40; // 209840 - 37.5% - 2015-01-26
if(nHeight > nMNPIBlock+(nMNPIPeriod* 4)) ret += blockValue / 40; // 227120 - 40.0% - 2015-02-27
if(nHeight > nMNPIBlock+(nMNPIPeriod* 5)) ret += blockValue / 40; // 244400 - 42.5% - 2015-03-30
if(nHeight > nMNPIBlock+(nMNPIPeriod* 6)) ret += blockValue / 40; // 261680 - 45.0% - 2015-05-01
if(nHeight > nMNPIBlock+(nMNPIPeriod* 7)) ret += blockValue / 40; // 278960 - 47.5% - 2015-06-01
if(nHeight > nMNPIBlock+(nMNPIPeriod* 9)) ret += blockValue / 40; // 313520 - 50.0% - 2015-08-03

if (nHeight < nReallocActivationHeight) {
// Block Reward Realocation is not activated yet, nothing to do
return ret;
}

int nSuperblockCycle = consensus_params.nSuperblockCycle;
// Actual realocation starts in the cycle next to one activation happens in
int nReallocStart = nReallocActivationHeight - nReallocActivationHeight % nSuperblockCycle + nSuperblockCycle;

if (nHeight < nReallocStart) {
// Activated but we have to wait for the next cycle to start realocation, nothing to do
return ret;
}

if (era != MnRewardEra::Classic) {
// Once MNRewardReallocated activates, block reward is 80% of block subsidy (+ tx fees) since treasury is 20%
// Since the MN reward needs to be equal to 60% of the block subsidy (according to the proposal), MN reward is set to 75% of the block reward.
// Previous reallocation periods are dropped.
return blockValue * 3 / 4;
}

// Periods used to reallocate the masternode reward from 50% to 60%
static std::vector<int> vecPeriods{
513, // Period 1: 51.3%
526, // Period 2: 52.6%
533, // Period 3: 53.3%
540, // Period 4: 54%
546, // Period 5: 54.6%
552, // Period 6: 55.2%
557, // Period 7: 55.7%
562, // Period 8: 56.2%
567, // Period 9: 56.7%
572, // Period 10: 57.2%
577, // Period 11: 57.7%
582, // Period 12: 58.2%
585, // Period 13: 58.5%
588, // Period 14: 58.8%
591, // Period 15: 59.1%
594, // Period 16: 59.4%
597, // Period 17: 59.7%
599, // Period 18: 59.9%
600 // Period 19: 60%
};

int nReallocCycle = nSuperblockCycle * 3;
int nCurrentPeriod = std::min<int>((nHeight - nReallocStart) / nReallocCycle, vecPeriods.size() - 1);

return static_cast<CAmount>(blockValue * vecPeriods[nCurrentPeriod] / 1000);
}

[[nodiscard]] bool CMNPaymentsProcessor::GetBlockTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
std::vector<CTxOut>& voutMasternodePaymentsRet)
MnRewardEra era, std::vector<CTxOut>& voutMasternodePaymentsRet)
{
voutMasternodePaymentsRet.clear();

const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;

bool fV20Active = DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_V20);
CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockSubsidy + feeReward, fV20Active);
CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockSubsidy + feeReward, m_consensus_params, era);

// Credit Pool doesn't exist before V20. If any part of reward will re-allocated to credit pool before v20
// activation these fund will be just permanently lost. Applicable for devnets, regtest, testnet
if (fV20Active && DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_MN_RR)) {
CAmount masternodeSubsidyReward = GetMasternodePayment(nBlockHeight, blockSubsidy, fV20Active);
if (era == MnRewardEra::EvoReward) {
CAmount masternodeSubsidyReward = GetMasternodePayment(nBlockHeight, blockSubsidy, m_consensus_params, era);
const CAmount platformReward = PlatformShare(masternodeSubsidyReward);
masternodeReward -= platformReward;

Expand Down Expand Up @@ -87,12 +154,12 @@ CAmount PlatformShare(const CAmount reward)
* Get masternode payment tx outputs
*/
[[nodiscard]] bool CMNPaymentsProcessor::GetMasternodeTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
std::vector<CTxOut>& voutMasternodePaymentsRet)
MnRewardEra era, std::vector<CTxOut>& voutMasternodePaymentsRet)
{
// make sure it's not filled yet
voutMasternodePaymentsRet.clear();

if(!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePaymentsRet)) {
if(!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, era, voutMasternodePaymentsRet)) {
LogPrintf("CMNPaymentsProcessor::%s -- ERROR Failed to get payee\n", __func__);
return false;
}
Expand All @@ -108,7 +175,7 @@ CAmount PlatformShare(const CAmount reward)
}

[[nodiscard]] bool CMNPaymentsProcessor::IsTransactionValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy,
const CAmount feeReward)
const CAmount feeReward, MnRewardEra era)
{
const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
if (!DeploymentDIP0003Enforced(nBlockHeight, m_consensus_params)) {
Expand All @@ -117,7 +184,7 @@ CAmount PlatformShare(const CAmount reward)
}

std::vector<CTxOut> voutMasternodePayments;
if (!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePayments)) {
if (!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, era, voutMasternodePayments)) {
LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Failed to get payees for block at height %s\n", __func__, nBlockHeight);
return true;
}
Expand Down Expand Up @@ -184,7 +251,7 @@ CAmount PlatformShare(const CAmount reward)
* - Other blocks are 10% lower in outgoing value, so in total, no extra coins are created
* - When non-superblocks are detected, the normal schedule should be maintained
*/
bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock)
bool CMNPaymentsProcessor::IsBlockValueValid(const CChain& active_chain, const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock)
{
bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward);

Expand All @@ -204,7 +271,7 @@ bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const int nBlo

LogPrint(BCLog::MNPAYMENTS, "block.vtx[0]->GetValueOut() %lld <= blockReward %lld\n", block.vtx[0]->GetValueOut(), blockReward);

CAmount nSuperblockMaxValue = blockReward + CSuperblock::GetPaymentsLimit(m_chainman.ActiveChain(), nBlockHeight);
CAmount nSuperblockMaxValue = blockReward + CSuperblock::GetPaymentsLimit(active_chain, nBlockHeight);
bool isSuperblockMaxValueMet = (block.vtx[0]->GetValueOut() <= nSuperblockMaxValue);

LogPrint(BCLog::GOBJECT, "block.vtx[0]->GetValueOut() %lld <= nSuperblockMaxValue %lld\n", block.vtx[0]->GetValueOut(), nSuperblockMaxValue);
Expand Down Expand Up @@ -249,7 +316,7 @@ bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const int nBlo
}

// this actually also checks for correct payees and not only amount
if (!m_superblocks.IsValidSuperblock(m_chainman.ActiveChain(), tip_mn_list, *block.vtx[0], nBlockHeight, blockReward)) {
if (!m_superblocks.IsValidSuperblock(active_chain, tip_mn_list, *block.vtx[0], nBlockHeight, blockReward)) {
// triggered but invalid? that's weird
LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid superblock detected at height %d: %s", __func__, nBlockHeight, block.vtx[0]->ToString()); /* Continued */
// should NOT allow invalid superblocks, when superblocks are enabled
Expand All @@ -261,12 +328,12 @@ bool CMNPaymentsProcessor::IsBlockValueValid(const CBlock& block, const int nBlo
return true;
}

bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, const bool check_superblock)
bool CMNPaymentsProcessor::IsBlockPayeeValid(const CChain& active_chain, const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, MnRewardEra era, const bool check_superblock)
{
const int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;

// Check for correct masternode payment
if (IsTransactionValid(txNew, pindexPrev, blockSubsidy, feeReward)) {
if (IsTransactionValid(txNew, pindexPrev, blockSubsidy, feeReward, era)) {
LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- Valid masternode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
} else {
LogPrintf("CMNPaymentsProcessor::%s -- ERROR! Invalid masternode payment detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); /* Continued */
Expand Down Expand Up @@ -294,7 +361,7 @@ bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CB

const auto tip_mn_list = m_dmnman.GetListAtChainTip();
if (m_superblocks.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) {
if (m_superblocks.IsValidSuperblock(m_chainman.ActiveChain(), tip_mn_list, txNew, nBlockHeight,
if (m_superblocks.IsValidSuperblock(active_chain, tip_mn_list, txNew, nBlockHeight,
blockSubsidy + feeReward)) {
LogPrint(BCLog::GOBJECT, "CMNPaymentsProcessor::%s -- Valid superblock at height %d: %s", /* Continued */
__func__, nBlockHeight, txNew.ToString());
Expand All @@ -313,7 +380,7 @@ bool CMNPaymentsProcessor::IsBlockPayeeValid(const CTransaction& txNew, const CB
}

void CMNPaymentsProcessor::FillBlockPayments(CMutableTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet)
MnRewardEra era, std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet)
{
int nBlockHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;

Expand All @@ -324,7 +391,7 @@ void CMNPaymentsProcessor::FillBlockPayments(CMutableTransaction& txNew, const C
m_superblocks.GetSuperblockPayments(tip_mn_list, nBlockHeight, voutSuperblockPaymentsRet);
}

if (!GetMasternodeTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePaymentsRet)) {
if (!GetMasternodeTxOuts(pindexPrev, blockSubsidy, feeReward, era, voutMasternodePaymentsRet)) {
LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- No masternode to pay (MN list probably empty)\n", __func__);
}

Expand Down
34 changes: 24 additions & 10 deletions src/masternode/payments.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

class CBlock;
class CBlockIndex;
class CChain;
class CDeterministicMNManager;
class ChainstateManager;
class CTransaction;
class CTxOut;

Expand All @@ -30,37 +30,51 @@ namespace Consensus { struct Params; }
*/
CAmount PlatformShare(const CAmount masternodeReward);

/**
* Masternode reward era for a given block, gating how the reward is computed.
* Each era implies the previous one: EvoReward is only reachable once CreditPool
* (V20) is active, so the ordering encodes that invariant. The era is computed by
* the caller (which owns the block context) and passed in, keeping this module free
* of deployment dependencies. Note this is orthogonal to DIP0003 enforcement, which
* gates whether payees are validated at all and is handled separately.
*/
enum class MnRewardEra {
Classic, // historical reward schedule, no credit pool
CreditPool, // V20: credit pool active, no platform reallocation yet
EvoReward, // MN_RR: platform share is reallocated from the masternode reward
};

CAmount GetMasternodePayment(int nHeight, CAmount blockValue, const Consensus::Params& consensus_params, MnRewardEra era);

class CMNPaymentsProcessor
{
private:
CDeterministicMNManager& m_dmnman;
governance::SuperblockManager& m_superblocks;
const ChainstateManager& m_chainman;
const Consensus::Params& m_consensus_params;

private:
[[nodiscard]] bool GetBlockTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
std::vector<CTxOut>& voutMasternodePaymentsRet);
MnRewardEra era, std::vector<CTxOut>& voutMasternodePaymentsRet);
[[nodiscard]] bool GetMasternodeTxOuts(const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
std::vector<CTxOut>& voutMasternodePaymentsRet);
MnRewardEra era, std::vector<CTxOut>& voutMasternodePaymentsRet);
[[nodiscard]] bool IsTransactionValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy,
const CAmount feeReward);
const CAmount feeReward, MnRewardEra era);
[[nodiscard]] bool IsOldBudgetBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet);

public:
explicit CMNPaymentsProcessor(CDeterministicMNManager& dmnman, governance::SuperblockManager& superblocks,
const ChainstateManager& chainman, const Consensus::Params& consensus_params) :
const Consensus::Params& consensus_params) :
m_dmnman{dmnman},
m_superblocks{superblocks},
m_chainman{chainman},
m_consensus_params{consensus_params}
{
}

bool IsBlockValueValid(const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock);
bool IsBlockPayeeValid(const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, const bool check_superblock);
bool IsBlockValueValid(const CChain& active_chain, const CBlock& block, const int nBlockHeight, const CAmount blockReward, std::string& strErrorRet, const bool check_superblock);
bool IsBlockPayeeValid(const CChain& active_chain, const CTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward, MnRewardEra era, const bool check_superblock);
void FillBlockPayments(CMutableTransaction& txNew, const CBlockIndex* pindexPrev, const CAmount blockSubsidy, const CAmount feeReward,
std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet);
MnRewardEra era, std::vector<CTxOut>& voutMasternodePaymentsRet, std::vector<CTxOut>& voutSuperblockPaymentsRet);
};

#endif // BITCOIN_MASTERNODE_PAYMENTS_H
3 changes: 2 additions & 1 deletion src/node/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc

// Update coinbase transaction with additional info about masternode and governance payments,
// get some info back to pass to getblocktemplate
m_chain_helper.mn_payments->FillBlockPayments(coinbaseTx, pindexPrev, blockSubsidy, nFees, pblocktemplate->voutMasternodePayments, pblocktemplate->voutSuperblockPayments);
const MnRewardEra mn_reward_era{GetMnRewardEraAfter(pindexPrev, m_chainstate.m_chainman)};
m_chain_helper.mn_payments->FillBlockPayments(coinbaseTx, pindexPrev, blockSubsidy, nFees, mn_reward_era, pblocktemplate->voutMasternodePayments, pblocktemplate->voutSuperblockPayments);

pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vTxFees[0] = -nFees;
Expand Down
3 changes: 2 additions & 1 deletion src/rpc/masternode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,8 @@ static RPCHelpMan masternode_payments()
std::vector<CTxOut> voutMasternodePayments, voutDummy;
CMutableTransaction dummyTx;
CAmount blockSubsidy = GetBlockSubsidy(pindex, Params().GetConsensus());
node.chain_helper->mn_payments->FillBlockPayments(dummyTx, pindex->pprev, blockSubsidy, nBlockFees, voutMasternodePayments, voutDummy);
const MnRewardEra mn_reward_era{GetMnRewardEraAfter(pindex->pprev, chainman)};
node.chain_helper->mn_payments->FillBlockPayments(dummyTx, pindex->pprev, blockSubsidy, nBlockFees, mn_reward_era, voutMasternodePayments, voutDummy);

UniValue blockObj(UniValue::VOBJ);
CAmount payedPerBlock{0};
Expand Down
Loading
Loading