Skip to content

Commit

Permalink
feat!: Block Reward Reallocation (Doubling Treasury) (dashpay#5588)
Browse files Browse the repository at this point in the history
## Issue being fixed or feature implemented
Implementation of accepted proposal:
https://www.dashcentral.org/p/TREASURY-REALLOCATION-60-20-20

## What was done?
Once Masternode Reward Location Reallocation activates:
- Treasury is bumped to 20% of block subsidy.
- Block reward shares are immediately set to 75% for MN and 25% miners.
(Previous reallocation periods are dropped)
MN reward share should be 75% of block reward in order to represent 60%
of the block subsidy. (according to the proposal)
- `governancebudget` is returned from `getgovernanceinfo` RPC.

## How Has This Been Tested?
`block_reward_reallocation_tests`

## Breaking Changes


## Checklist:
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have added or updated relevant unit/integration/functional/e2e
tests
- [x] I have made corresponding changes to the documentation
- [x] I have assigned this pull request to a milestone _(for repository
code-owners and collaborators only)_

---------

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
  • Loading branch information
ogabrielides and UdjinM6 authored Oct 3, 2023
1 parent 39412cf commit e72eb40
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 29 deletions.
14 changes: 14 additions & 0 deletions doc/release-notes-5588.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Block Reward Reallocation
-------------------------

Once Masternode Reward Location Reallocation activates:

- Treasury is bumped to 20% of block subsidy.
- Block reward shares are immediately set to 60% for MN and 20% miners.

Note: Previous reallocation periods are dropped.

Updated RPCs
------------

- `getgovernanceinfo` RPC returns the field `governancebudget`: the governance budget for the next superblock.
2 changes: 1 addition & 1 deletion src/bench/duplicate_inputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static void DuplicateInputs(benchmark::Bench& bench)
coinbaseTx.vin[0].prevout.SetNull();
coinbaseTx.vout.resize(1);
coinbaseTx.vout[0].scriptPubKey = SCRIPT_PUB;
coinbaseTx.vout[0].nValue = GetBlockSubsidyInner(block.nBits, nHeight, chainparams.GetConsensus());
coinbaseTx.vout[0].nValue = GetBlockSubsidyInner(block.nBits, nHeight, chainparams.GetConsensus(), false);
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;

naughtyTx.vout.resize(1);
Expand Down
2 changes: 1 addition & 1 deletion src/evo/creditpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ bool CCreditPoolDiff::SetTarget(const CTransaction& tx, TxValidationState& state
for (const CTxOut& txout : tx.vout) {
blockReward += txout.nValue;
}
platformReward = MasternodePayments::PlatformShare(GetMasternodePayment(cbTx.nHeight, blockReward, params.BRRHeight));
platformReward = MasternodePayments::PlatformShare(GetMasternodePayment(cbTx.nHeight, blockReward, params.BRRHeight, true));
LogPrintf("CreditPool: set target to %lld with MN reward %lld\n", *targetBalance, platformReward);

return true;
Expand Down
15 changes: 14 additions & 1 deletion src/governance/classes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#include <governance/classes.h>

#include <chainparams.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <governance/governance.h>
#include <key_io.h>
#include <llmq/utils.h>
#include <primitives/transaction.h>
#include <script/standard.h>
#include <timedata.h>
Expand Down Expand Up @@ -492,10 +494,21 @@ CAmount CSuperblock::GetPaymentsLimit(int nBlockHeight)
return 0;
}

const CBlockIndex* tipIndex = ::ChainActive().Tip();
bool fMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(tipIndex);
if (!fMNRewardReallocated && nBlockHeight > tipIndex->nHeight) {
// If fMNRewardReallocated isn't active yet and nBlockHeight refers to a future SuperBlock
// then we need to check if the fork is locked_in and see if it will be active by the time of the future SuperBlock
if (llmq::utils::GetMNRewardReallocationState(tipIndex) == ThresholdState::LOCKED_IN) {
int activation_height = llmq::utils::GetMNRewardReallocationSince(tipIndex) + static_cast<int>(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize);
if (nBlockHeight >= activation_height) fMNRewardReallocated = true;
}
}

// min subsidy for high diff networks and vice versa
int nBits = consensusParams.fPowAllowMinDifficultyBlocks ? UintToArith256(consensusParams.powLimit).GetCompact() : 1;
// some part of all blocks issued during the cycle goes to superblock, see GetBlockSubsidy
CAmount nSuperblockPartOfSubsidy = GetBlockSubsidyInner(nBits, nBlockHeight - 1, consensusParams, true);
CAmount nSuperblockPartOfSubsidy = GetBlockSubsidyInner(nBits, nBlockHeight - 1, consensusParams, fMNRewardReallocated,true);
CAmount nPaymentsLimit = nSuperblockPartOfSubsidy * consensusParams.nSuperblockCycle;
LogPrint(BCLog::GOBJECT, "CSuperblock::GetPaymentsLimit -- Valid superblock height %d, payments max %lld\n", nBlockHeight, nPaymentsLimit);

Expand Down
14 changes: 14 additions & 0 deletions src/llmq/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,20 @@ bool IsMNRewardReallocationActive(const CBlockIndex* pindex)
return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR, llmq_versionbitscache) == ThresholdState::ACTIVE;
}

ThresholdState GetMNRewardReallocationState(const CBlockIndex* pindex)
{
assert(pindex);
LOCK(cs_llmq_vbc);
return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR, llmq_versionbitscache);
}

int GetMNRewardReallocationSince(const CBlockIndex* pindex)
{
assert(pindex);
LOCK(cs_llmq_vbc);
return VersionBitsStateSinceHeight(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR, llmq_versionbitscache);
}

bool IsInstantSendLLMQTypeShared()
{
if (Params().GetConsensus().llmqTypeInstantSend == Params().GetConsensus().llmqTypeChainLocks ||
Expand Down
2 changes: 2 additions & 0 deletions src/llmq/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ bool IsV19Active(const CBlockIndex* pindex);
const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex);
bool IsV20Active(const CBlockIndex* pindex);
bool IsMNRewardReallocationActive(const CBlockIndex* pindex);
ThresholdState GetMNRewardReallocationState(const CBlockIndex* pindex);
int GetMNRewardReallocationSince(const CBlockIndex* pindex);

/// Returns the state of `-llmq-data-recovery`
bool QuorumDataRecoveryEnabled();
Expand Down
3 changes: 1 addition & 2 deletions src/masternode/payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@
{
voutMasternodePaymentsRet.clear();

CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward, Params().GetConsensus().BRRHeight);

const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[nBlockHeight - 1]);
bool fMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(pindex);

CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward, Params().GetConsensus().BRRHeight, fMNRewardReallocated);
if (fMNRewardReallocated) {
const CAmount platformReward = MasternodePayments::PlatformShare(masternodeReward);
masternodeReward -= platformReward;
Expand Down
6 changes: 3 additions & 3 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;

// NOTE: unlike in bitcoin, we need to pass PREVIOUS block height here
CAmount blockReward = nFees + GetBlockSubsidyInner(pindexPrev->nBits, pindexPrev->nHeight, Params().GetConsensus());
bool fMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(pindexPrev);
CAmount blockReward = nFees + GetBlockSubsidyInner(pindexPrev->nBits, pindexPrev->nHeight, Params().GetConsensus(), fMNRewardReallocated);

// Compute regular coinbase transaction.
coinbaseTx.vout[0].nValue = blockReward;
Expand Down Expand Up @@ -232,9 +233,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
}
assert(creditPoolDiff != std::nullopt);

bool fMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(pindexPrev);
if (fMNRewardReallocated) {
const CAmount masternodeReward = GetMasternodePayment(nHeight, blockReward, Params().GetConsensus().BRRHeight);
const CAmount masternodeReward = GetMasternodePayment(nHeight, blockReward, Params().GetConsensus().BRRHeight, fMNRewardReallocated);
const CAmount reallocedReward = MasternodePayments::PlatformShare(masternodeReward);
LogPrint(BCLog::MNPAYMENTS, "%s: add MN reward %lld (%lld) to credit pool\n", __func__, masternodeReward, reallocedReward);
creditPoolDiff->AddRewardRealloced(reallocedReward);
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/governance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,7 @@ static UniValue getgovernanceinfo(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "lastsuperblock", "the block number of the last superblock"},
{RPCResult::Type::NUM, "nextsuperblock", "the block number of the next superblock"},
{RPCResult::Type::NUM, "fundingthreshold", "the number of absolute yes votes required for a proposal to be passing"},
{RPCResult::Type::NUM, "governancebudget", "the governance budget for the next superblock in " + CURRENCY_UNIT + ""},
}},
RPCExamples{
HelpExampleCli("getgovernanceinfo", "")
Expand All @@ -1047,6 +1048,7 @@ static UniValue getgovernanceinfo(const JSONRPCRequest& request)
obj.pushKV("lastsuperblock", nLastSuperblock);
obj.pushKV("nextsuperblock", nNextSuperblock);
obj.pushKV("fundingthreshold", int(deterministicMNManager->GetListAtChainTip().GetValidWeightedMNsCount() / 10));
obj.pushKV("governancebudget", ValueFromAmount(CSuperblock::GetPaymentsLimit(nNextSuperblock)));

return obj;
}
Expand Down
35 changes: 23 additions & 12 deletions src/test/block_reward_reallocation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
auto tx = CreateProRegTx(*m_node.mempool, utxos, 1, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey);

CreateAndProcessBlock({tx}, coinbaseKey);
// Will be updated later
bool isMNRewardReallocated = false;

{
LOCK(cs_main);
Expand Down Expand Up @@ -203,7 +205,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
LOCK(cs_main);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params, isMNRewardReallocated), 2500, isMNRewardReallocated);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
}
Expand All @@ -214,7 +216,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS

{
LOCK(cs_main);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params, isMNRewardReallocated), 2500, isMNRewardReallocated);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 13748571607);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
Expand All @@ -229,7 +231,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
CreateAndProcessBlock({}, coinbaseKey);
}
LOCK(cs_main);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params, isMNRewardReallocated), 2500, isMNRewardReallocated);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
}
Expand All @@ -238,12 +240,13 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
{
// Reward split should reach ~60/40 after reallocation is done
LOCK(cs_main);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
const CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params, isMNRewardReallocated), 2500, isMNRewardReallocated);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 10221599170);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 6132959502); // 0.6
}
BOOST_CHECK(!llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip()));

// Reward split should stay ~60/40 after reallocation is done,
// check 10 next superblocks
Expand All @@ -252,11 +255,10 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
CreateAndProcessBlock({}, coinbaseKey);
}
LOCK(cs_main);

CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
isMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip());
CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params, isMNRewardReallocated), 2500, isMNRewardReallocated);
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);

bool isMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip());
if (isMNRewardReallocated) {
const CAmount platform_payment = MasternodePayments::PlatformShare(masternode_payment);
masternode_payment -= platform_payment;
Expand All @@ -266,19 +268,28 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[payment_index].nValue, masternode_payment);
}

BOOST_CHECK(llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip()));
{ // At this moment Masternode reward should be reallocated to platform
// Reward split should reach ~60/40 after reallocation is done
// Allocation of block subsidy is 60% MN, 20% miners and 20% treasury
LOCK(cs_main);
CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
isMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip());
CAmount masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidyInner(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params, isMNRewardReallocated), 2500, isMNRewardReallocated);
const CAmount platform_payment = MasternodePayments::PlatformShare(masternode_payment);
masternode_payment -= platform_payment;
const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx, *m_node.evodb, ::ChainstateActive(), *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 9491484944);

BOOST_CHECK(llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip()));
// At this height (3178) the block subsidy is 10546094382.
CAmount block_subsidy = CAmount(10546094382);
// Treasury is 20% since MNRewardReallocation
CAmount expected_block_reward = block_subsidy - block_subsidy / 5;
// Since MNRewardReallocation, MN reward share is 75% of the block reward
CAmount expected_masternode_reward = expected_block_reward * 3 / 4;
CAmount expected_mn_platform_payment = MasternodePayments::PlatformShare(expected_masternode_reward);
CAmount expected_mn_core_payment = expected_masternode_reward - expected_mn_platform_payment;

BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), expected_block_reward);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[1].nValue, masternode_payment);
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[1].nValue, 3559306854); // 0.6
BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[1].nValue, expected_mn_core_payment);
}
}

Expand Down
21 changes: 16 additions & 5 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

#include <llmq/instantsend.h>
#include <llmq/chainlocks.h>
#include <llmq/utils.h>

#include <statsd_client.h>

Expand Down Expand Up @@ -1113,7 +1114,7 @@ NOTE: unlike bitcoin we are using PREVIOUS block height here,
might be a good idea to change this to use prev bits
but current height to avoid confusion.
*/
CAmount GetBlockSubsidyInner(int nPrevBits, int nPrevHeight, const Consensus::Params& consensusParams, bool fSuperblockPartOnly)
CAmount GetBlockSubsidyInner(int nPrevBits, int nPrevHeight, const Consensus::Params& consensusParams, bool fMNRewardReallocated, bool fSuperblockPartOnly)
{
double dDiff;
CAmount nSubsidyBase;
Expand Down Expand Up @@ -1157,19 +1158,22 @@ CAmount GetBlockSubsidyInner(int nPrevBits, int nPrevHeight, const Consensus::Pa
nSubsidy *= consensusParams.nHighSubsidyFactor;
}

CAmount nSuperblockPart{};
// Hard fork to reduce the block reward by 10 extra percent (allowing budget/superblocks)
CAmount nSuperblockPart = (nPrevHeight > consensusParams.nBudgetPaymentsStartBlock) ? nSubsidy/10 : 0;

if (nPrevHeight > consensusParams.nBudgetPaymentsStartBlock) {
// Once MNRewardReallocated is active, the treasury is 20% instead of 10%
nSuperblockPart = nSubsidy / (fMNRewardReallocated ? 5 : 10);
}
return fSuperblockPartOnly ? nSuperblockPart : nSubsidy - nSuperblockPart;
}

CAmount GetBlockSubsidy(const CBlockIndex* const pindex, const Consensus::Params& consensusParams)
{
if (pindex->pprev == nullptr) return Params().GenesisBlock().vtx[0]->GetValueOut();
return GetBlockSubsidyInner(pindex->pprev->nBits, pindex->pprev->nHeight, consensusParams);
return GetBlockSubsidyInner(pindex->pprev->nBits, pindex->pprev->nHeight, consensusParams, llmq::utils::IsMNRewardReallocationActive(pindex->pprev));
}

CAmount GetMasternodePayment(int nHeight, CAmount blockValue, int nReallocActivationHeight)
CAmount GetMasternodePayment(int nHeight, CAmount blockValue, int nReallocActivationHeight, bool fMNRewardReallocated)
{
CAmount ret = blockValue/5; // start at 20%

Expand Down Expand Up @@ -1201,6 +1205,13 @@ CAmount GetMasternodePayment(int nHeight, CAmount blockValue, int nReallocActiva
return ret;
}

if (fMNRewardReallocated) {
// 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%
Expand Down
Loading

0 comments on commit e72eb40

Please sign in to comment.