Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Block Reward Reallocation (Doubling Treasury) #5588

Merged
merged 9 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
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 75% for MN and 25% miners. MN reward share should be 75% of block reward in order to represent 60% of the block subsidy. (according to the proposal).
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved

Note: Previous reallocation periods are dropped.

Updated RPCs
--------
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved

- `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
14 changes: 13 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,20 @@ CAmount CSuperblock::GetPaymentsLimit(int nBlockHeight)
return 0;
}

bool fMNRewardReallocated = llmq::utils::IsMNRewardReallocationActive(::ChainActive().Tip());
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
if (!fMNRewardReallocated && nBlockHeight > ::ChainActive().Tip()->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(::ChainActive().Tip()) == ThresholdState::LOCKED_IN) {
int activation_height = llmq::utils::GetMNRewardReallocationSince(::ChainActive().Tip()) + 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
39 changes: 26 additions & 13 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,30 @@ 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 * 0.8;
// Transaction fee
expected_block_reward += 1;
// Since MNRewardReallocation, MN reward share is 75% of the block reward
CAmount expected_masternode_reward = expected_block_reward * 0.75;
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
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
22 changes: 17 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,23 @@ 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) {
nSuperblockPart = nSubsidy / 10;
// Once MNRewardReallocated is active, the treasury is 20% instead of 10%
if (fMNRewardReallocated) nSuperblockPart *= 2;
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
}
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 +1206,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 * 0.75;
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
}

// Periods used to reallocate the masternode reward from 50% to 60%
static std::vector<int> vecPeriods{
513, // Period 1: 51.3%
Expand Down
Loading
Loading