Skip to content

Commit

Permalink
Tokenomics update (#1478)
Browse files Browse the repository at this point in the history
* Change of emission rules

* Fixes for testnet

* Cleaning up code and tests

* Workaround for current devnet bugs

* Workaround for testnet

* Devnet parameter tweak

* Changed HF block for testnet

* Fix for tests

* Tweaked regtest parameter to fix tests
  • Loading branch information
psolstice authored Sep 3, 2024
1 parent 30b8595 commit bfd992e
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 124 deletions.
38 changes: 29 additions & 9 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ class CMainParams : public CChainParams {
consensus.nSubsidyHalvingFirst = 302438;
consensus.nSubsidyHalvingSecond = AdjustEndingBlockNumberAfterSubsidyHalving(302438, 420000, 486221); // =958655
consensus.nSubsidyHalvingInterval = 420000*2;
consensus.nSubsidyHalvingStopBlock = AdjustEndingBlockNumberAfterSubsidyHalving(0, 3646849, 486221); // =6807477

consensus.stage2DevelopmentFundShare = 15;
consensus.stage2ZnodeShare = 35;
Expand All @@ -206,6 +205,12 @@ class CMainParams : public CChainParams {
consensus.stage3DevelopmentFundAddress = "aLgRaYSFk6iVw2FqY1oei8Tdn2aTsGPVmP";
consensus.stage3CommunityFundAddress = "aFA2TbqG9cnhhzX5Yny2pBJRK5EaEqLCH7";

consensus.stage4StartBlock = consensus.nSubsidyHalvingSecond;
consensus.stage4CommunityFundShare = 10;
consensus.stage4DevelopmentFundShare = 15;
consensus.stage4MasternodeShare = 70;
consensus.tailEmissionBlockSubsidy = 4 * COIN; // real value would be 1 FIRO (because of two halvings due to different block times)

consensus.nStartBlacklist = 293990;
consensus.nStartDuplicationCheck = 293526;

Expand Down Expand Up @@ -514,7 +519,6 @@ class CTestNetParams : public CChainParams {
consensus.nSubsidyHalvingFirst = 12000;
consensus.nSubsidyHalvingSecond = 150000;
consensus.nSubsidyHalvingInterval = 150000;
consensus.nSubsidyHalvingStopBlock = 1000000;

consensus.stage2DevelopmentFundShare = 15;
consensus.stage2ZnodeShare = 35;
Expand All @@ -528,6 +532,12 @@ class CTestNetParams : public CChainParams {
consensus.stage3DevelopmentFundAddress = "TWDxLLKsFp6qcV1LL4U2uNmW4HwMcapmMU";
consensus.stage3CommunityFundAddress = "TCkC4uoErEyCB4MK3d6ouyJELoXnuyqe9L";

consensus.stage4StartBlock = 167500;
consensus.stage4CommunityFundShare = 10;
consensus.stage4DevelopmentFundShare = 15;
consensus.stage4MasternodeShare = 70;
consensus.tailEmissionBlockSubsidy = 4 * COIN; // real value would be 1 FIRO (because of two halvings due to different block times)

consensus.nStartBlacklist = 0;
consensus.nStartDuplicationCheck = 0;
consensus.nMajorityEnforceBlockUpgrade = 51;
Expand Down Expand Up @@ -789,22 +799,27 @@ class CDevNetParams : public CChainParams {
consensus.chainType = Consensus::chainDevnet;

consensus.nSubsidyHalvingFirst = 1;
consensus.nSubsidyHalvingSecond = 100000;
consensus.nSubsidyHalvingInterval = 100000;
consensus.nSubsidyHalvingStopBlock = 1000000;
consensus.nSubsidyHalvingSecond = 3000;
consensus.nSubsidyHalvingInterval = 10000;

consensus.stage2DevelopmentFundShare = 15;
consensus.stage2ZnodeShare = 35;
consensus.stage2DevelopmentFundAddress = "Tq99tes2sRbQ1yNUJPJ7BforYnKcitgwWq";

consensus.stage3StartTime = 1653382800;
consensus.stage3StartBlock = 1514;
consensus.stage3StartBlock = 1514; // this is incorrect value but we have to leave it for now
consensus.stage3DevelopmentFundShare = 15;
consensus.stage3CommunityFundShare = 10;
consensus.stage3MasternodeShare = 50;
consensus.stage3DevelopmentFundAddress = "TfvbHyGTo8hexoKBBS8fz9Gq7g9VZQQpcg";
consensus.stage3CommunityFundAddress = "TgoL9nh8vDTz7UB5WkBbknBksBdUaD9qbT";

consensus.stage4StartBlock = consensus.nSubsidyHalvingSecond;
consensus.stage4CommunityFundShare = 10;
consensus.stage4DevelopmentFundShare = 15;
consensus.stage4MasternodeShare = 70;
consensus.tailEmissionBlockSubsidy = 4 * COIN; // real value would be 1 FIRO (because of two halvings due to different block times)

consensus.nStartBlacklist = 0;
consensus.nStartDuplicationCheck = 0;
consensus.nMajorityEnforceBlockUpgrade = 51;
Expand Down Expand Up @@ -1031,21 +1046,26 @@ class CRegTestParams : public CChainParams {
consensus.nSubsidyHalvingFirst = 1500;
consensus.nSubsidyHalvingSecond = 2500;
consensus.nSubsidyHalvingInterval = 1000;
consensus.nSubsidyHalvingStopBlock = 10000;

consensus.nStartBlacklist = 0;
consensus.nStartDuplicationCheck = 0;
consensus.stage2DevelopmentFundShare = 15;
consensus.stage2ZnodeShare = 35;

consensus.stage3StartTime = INT_MAX;
consensus.stage3StartBlock = 0;
consensus.stage3StartTime = INT_MAX; // tests should set this value individually
consensus.stage3StartBlock = INT_MAX; // same as above
consensus.stage3DevelopmentFundShare = 15;
consensus.stage3CommunityFundShare = 10;
consensus.stage3MasternodeShare = 50;
consensus.stage3DevelopmentFundAddress = "TGEGf26GwyUBE2P2o2beBAfE9Y438dCp5t"; // private key cMrz8Df36VR9TvZjtvSqLPhUQR7pcpkXRXaLNYUxfkKsRuCzHpAN
consensus.stage3CommunityFundAddress = "TJmPzeJF4DECrBwUftc265U7rTPxKmpa4F"; // private key cTyPWqTMM1CgT5qy3K3LSgC1H6Q2RHvnXZHvjWtKB4vq9qXqKmMu

consensus.stage4StartBlock = consensus.nSubsidyHalvingSecond;
consensus.stage4CommunityFundShare = 15;
consensus.stage4DevelopmentFundShare = 25;
consensus.stage4MasternodeShare = 50;
consensus.tailEmissionBlockSubsidy = 4 * COIN; // real value would be 1 FIRO (because of two halvings due to different block times)

consensus.nMajorityEnforceBlockUpgrade = 750;
consensus.nMajorityRejectBlockOutdated = 950;
consensus.nMajorityWindow = 1000;
Expand Down
15 changes: 13 additions & 2 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ struct Params {
int nSubsidyHalvingSecond;
/** Subsequent subsidy halving intervals */
int nSubsidyHalvingInterval;
/** Stop subsidy at this block number */
int nSubsidyHalvingStopBlock;

/** parameters for coinbase payment distribution between first halving and stage 3 (aka stage 2) */
/** P2PKH or P2SH address for developer funds */
Expand All @@ -163,6 +161,19 @@ struct Params {
/** percentage of block subsidy going to masternode */
int stage3MasternodeShare;

/** parameters for coinbase payment distribution after stage three (aka stage 4) */
/** start time of stage 4 (usually the same as nSubsidyHalvingSecond)*/
int stage4StartBlock;
/** percentage of block subsidy going to developer fund */
int stage4DevelopmentFundShare;
/** percentage of block subsidy going to community fund */
int stage4CommunityFundShare;
/** percentage of block subsidy going to masternode */
int stage4MasternodeShare;

/** tail emission (after stage 4) */
int tailEmissionBlockSubsidy;

int nStartDuplicationCheck;
int nStartBlacklist;

Expand Down
30 changes: 21 additions & 9 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc

pblock->nTime = GetAdjustedTime();
bool fMTP = (pblock->nTime >= params.nMTPSwitchTime);
bool fShorterBlockDistance = (pblock->nTime >= params.stage3StartTime);
bool fShorterBlockDistance = nHeight >= params.stage3StartBlock;
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();

pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()) | (fMTP ? 0x1000 : 0);
Expand Down Expand Up @@ -835,19 +835,31 @@ void BlockAssembler::FillFoundersReward(CMutableTransaction &coinbaseTx, bool fM
if (fShorterBlockDistance)
coin /= 2;

if (nHeight >= params.nSubsidyHalvingFirst && nHeight < params.nSubsidyHalvingSecond) {
if ((nHeight >= params.nSubsidyHalvingFirst && nHeight < params.nSubsidyHalvingSecond) || nHeight >= params.stage4StartBlock) {
if (fShorterBlockDistance) {
// Stage 3
bool fStage3 = nHeight < params.nSubsidyHalvingSecond;
bool fStage4 = nHeight >= params.stage4StartBlock;
CAmount devPayoutValue = 0, communityPayoutValue = 0;
CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(params.stage3DevelopmentFundAddress).Get());
CAmount devPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP, true) * params.stage3DevelopmentFundShare) / 100;
CScript communityPayoutScript = GetScriptForDestination(CBitcoinAddress(params.stage3CommunityFundAddress).Get());
CAmount communityPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP, true) * params.stage3CommunityFundShare) / 100;

coinbaseTx.vout[0].nValue -= devPayoutValue;
coinbaseTx.vout.push_back(CTxOut(devPayoutValue, devPayoutScript));
// There is no dev/community payout for testnet for some time
if (fStage3 || fStage4) {
int devShare = fStage3 ? params.stage3DevelopmentFundShare : params.stage4DevelopmentFundShare;
int communityShare = fStage3 ? params.stage3CommunityFundShare : params.stage4CommunityFundShare;
devPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP, true) * devShare) / 100;
communityPayoutValue = (GetBlockSubsidyWithMTPFlag(nHeight, params, fMTP, true) * communityShare) / 100;
}

coinbaseTx.vout[0].nValue -= communityPayoutValue;
coinbaseTx.vout.push_back(CTxOut(communityPayoutValue, communityPayoutScript));
if (devPayoutValue != 0) {
coinbaseTx.vout[0].nValue -= devPayoutValue;
coinbaseTx.vout.push_back(CTxOut(devPayoutValue, devPayoutScript));
}

if (communityPayoutValue != 0) {
coinbaseTx.vout[0].nValue -= communityPayoutValue;
coinbaseTx.vout.push_back(CTxOut(communityPayoutValue, communityPayoutScript));
}
}
else {
// Stage 2
Expand Down
45 changes: 23 additions & 22 deletions src/test/firsthalving_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,8 @@ BOOST_FIXTURE_TEST_CASE(devpayout, TestChainDIP3BeforeActivationSetup)

consensusParams.nSubsidyHalvingFirst = 600;
consensusParams.stage3StartTime = INT_MAX;
consensusParams.nSubsidyHalvingSecond = 620;
consensusParams.nSubsidyHalvingSecond = consensusParams.stage4StartBlock = 620;
consensusParams.nSubsidyHalvingInterval = 10;
consensusParams.nSubsidyHalvingStopBlock = 1000;

CScript devPayoutScript = GenerateRandomAddress();
CTxDestination devPayoutDest{CScriptID(devPayoutScript)};
Expand Down Expand Up @@ -226,6 +225,8 @@ BOOST_FIXTURE_TEST_CASE(devpayout, TestChainDIP3BeforeActivationSetup)

// initiate stage3
consensusParams.stage3StartTime = GetTime();
consensusParams.stage3StartBlock = chainActive.Height();

// for stage3 there is dev payout and community payout
for (int i=610; i<620; i++) {
CBlock block = CreateAndProcessBlock({}, coinbaseKey);
Expand Down Expand Up @@ -260,39 +261,39 @@ BOOST_FIXTURE_TEST_CASE(devpayout, TestChainDIP3BeforeActivationSetup)
CAmount nValue;
auto dmnPayout = FindPayoutDmn(block, nValue);

BOOST_ASSERT(dmnPayout != nullptr && nValue == 3125*COIN/1000); // 3.125 (6.25*0.5)
// stage 4: no real halving here
BOOST_ASSERT(dmnPayout != nullptr && nValue == 625*COIN/100); // 6.25

// there should be no more payment to devs fund
for (const CTxOut &txout: block.vtx[0]->vout) {
BOOST_ASSERT(txout.scriptPubKey != GetScriptForDestination(CBitcoinAddress(consensusParams.stage2DevelopmentFundAddress).Get()));
}

// miner's reward should be 6.25-3.125 = 3.125
BOOST_ASSERT(block.vtx[0]->vout[0].nValue == 3125*COIN/1000);
// should be only 2 vouts in coinbase
BOOST_ASSERT(block.vtx[0]->vout.size() == 2);
// miner's reward should be 1.25 (10%)
BOOST_ASSERT(block.vtx[0]->vout[0].nValue == 125*COIN/100);

bool paymentToDevFound = false, paymentToCommunityFound = false;
for (const CTxOut &txout: block.vtx[0]->vout) {
if (txout.scriptPubKey == GetScriptForDestination(CBitcoinAddress(consensusParams.stage3DevelopmentFundAddress).Get())) {
BOOST_ASSERT(txout.nValue == 3125*COIN/1000); // 25/2*0.25
paymentToDevFound = true;
}
if (txout.scriptPubKey == GetScriptForDestination(CBitcoinAddress(consensusParams.stage3CommunityFundAddress).Get())) {
BOOST_ASSERT(txout.nValue == 1875*COIN/1000); // 25/2*0.15
paymentToCommunityFound = true;
}
}
BOOST_ASSERT(paymentToDevFound && paymentToCommunityFound);
}

// the third halving should occur at block 630
// tail emission should occur at block 630
CBlock block = CreateAndProcessBlock({}, coinbaseKey);
deterministicMNManager->UpdatedBlockTip(chainActive.Tip());

CAmount nValue;
auto dmnPayout = FindPayoutDmn(block, nValue);

BOOST_ASSERT(dmnPayout != nullptr && nValue == 3125*COIN/1000/2); // 3.125/2 (3.125*0.5)

// there should be no more payment to devs/community funds fund
for (const CTxOut &txout: block.vtx[0]->vout) {
BOOST_ASSERT(txout.scriptPubKey != GetScriptForDestination(CBitcoinAddress(consensusParams.stage2DevelopmentFundAddress).Get()) &&
txout.scriptPubKey != GetScriptForDestination(CBitcoinAddress(consensusParams.stage3DevelopmentFundAddress).Get()) &&
txout.scriptPubKey != GetScriptForDestination(CBitcoinAddress(consensusParams.stage3CommunityFundAddress).Get()));
}

// miner's reward should be 3.125/2
BOOST_ASSERT(block.vtx[0]->vout[0].nValue == 3125*COIN/1000/2);
// should be only 2 vouts in coinbase
BOOST_ASSERT(block.vtx[0]->vout.size() == 2);
BOOST_ASSERT(dmnPayout != nullptr && nValue == COIN);

consensusParams = consensusParamsBackup;
}
Expand All @@ -304,8 +305,8 @@ BOOST_FIXTURE_TEST_CASE(devpayoutverification, TestChainDIP3BeforeActivationSetu

consensusParams.nSubsidyHalvingFirst = 600;
consensusParams.nSubsidyHalvingSecond = 610;
consensusParams.stage3StartTime = INT_MAX;
consensusParams.nSubsidyHalvingInterval = 10;
consensusParams.nSubsidyHalvingStopBlock = 1000;

// skip to block 600
for (int i=498; i<600; i++)
Expand Down
53 changes: 6 additions & 47 deletions src/test/main_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,13 @@ static void TestBlockSubsidyHalvings(const Consensus::Params& consensusParams)
BOOST_CHECK_EQUAL(GetBlockSubsidy(1, consensusParams, consensusParams.nMTPSwitchTime-1000), nInitialSubsidy);
nInitialSubsidy /= consensusParams.nMTPRewardReduction;
BOOST_CHECK_EQUAL(GetBlockSubsidy(2, consensusParams, consensusParams.nMTPSwitchTime), nInitialSubsidy);
CAmount baseSubsidy = nInitialSubsidy;

CAmount nPreviousSubsidy = nInitialSubsidy;
for (int nHalvings = 1; nHalvings < maxHalvings; nHalvings++) {
int nHeight = consensusParams.nSubsidyHalvingFirst + (nHalvings-1) * consensusParams.nSubsidyHalvingInterval;
if (nHeight >= consensusParams.nSubsidyHalvingStopBlock)
break;
CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams, consensusParams.nMTPSwitchTime);
BOOST_CHECK(nSubsidy <= nInitialSubsidy);
if(nHeight > 0)
BOOST_CHECK_EQUAL(nSubsidy, nPreviousSubsidy / 2);
nPreviousSubsidy = nPreviousSubsidy / 2;
}
BOOST_CHECK_EQUAL(GetBlockSubsidy(consensusParams.nSubsidyHalvingStopBlock, consensusParams), 0);
BOOST_CHECK_EQUAL(GetBlockSubsidy(consensusParams.nSubsidyHalvingFirst, consensusParams, consensusParams.nMTPSwitchTime), baseSubsidy/2);
BOOST_CHECK_EQUAL(GetBlockSubsidy(consensusParams.stage3StartBlock, consensusParams, consensusParams.stage3StartTime), baseSubsidy/4);
BOOST_CHECK_EQUAL(GetBlockSubsidy(consensusParams.nSubsidyHalvingSecond, consensusParams, consensusParams.stage3StartTime), baseSubsidy/4);
BOOST_CHECK_EQUAL(GetBlockSubsidy(consensusParams.nSubsidyHalvingSecond + consensusParams.nSubsidyHalvingInterval,
consensusParams, consensusParams.stage3StartTime), consensusParams.tailEmissionBlockSubsidy/consensusParams.nMTPRewardReduction/2);
}

BOOST_AUTO_TEST_CASE(block_subsidy_test)
Expand All @@ -42,41 +36,6 @@ BOOST_AUTO_TEST_CASE(block_subsidy_test)
//TestBlockSubsidyHalvings(1000); // Just another interval
}

BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
Consensus::Params consensusParams = Params(CBaseChainParams::MAIN).GetConsensus();
CAmount nSum = 0;
int lastHalving = (consensusParams.nSubsidyHalvingStopBlock - consensusParams.nSubsidyHalvingSecond)/consensusParams.nSubsidyHalvingInterval;
int lastHalvingBlock = consensusParams.nSubsidyHalvingSecond + lastHalving*consensusParams.nSubsidyHalvingInterval;

int step = 1;

for(int nHeight = 0; nHeight < 14000000; nHeight += step)
{
if (nHeight == consensusParams.nSubsidyHalvingSecond)
step = 1000;
else if (nHeight == lastHalvingBlock)
step = 1;
else if (nHeight == consensusParams.nSubsidyHalvingStopBlock)
step = 10000;

int nTime;
if (nHeight < consensusParams.nMTPStartBlock)
nTime = consensusParams.nMTPSwitchTime-1000;
else if (nHeight < consensusParams.stage3StartBlock)
nTime = consensusParams.stage3StartTime-1000;
else
nTime = consensusParams.stage3StartTime;
CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams, nTime);
if (nHeight == 0)
nSubsidy = 50*COIN;
BOOST_CHECK(nSubsidy <= 50 * COIN);
nSum += nSubsidy * step;
BOOST_CHECK(MoneyRange(nSum));
}
BOOST_CHECK_EQUAL(nSum, 2095751200767464ULL);
}

bool ReturnFalse() { return false; }
bool ReturnTrue() { return true; }

Expand Down
15 changes: 14 additions & 1 deletion src/test/mtp_halving_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,17 @@ CScript scriptPubKeyMtpHalving;


struct MtpHalvingTestingSetup : public TestingSetup {
MtpHalvingTestingSetup() : TestingSetup(CBaseChainParams::REGTEST)
Consensus::Params &mutableParams;
Consensus::Params oldParams;

MtpHalvingTestingSetup() : TestingSetup(CBaseChainParams::REGTEST), mutableParams(const_cast<Consensus::Params&>(Params().GetConsensus()))
{
oldParams = mutableParams;

// disable stage 3 stuff for now
mutableParams.stage3StartTime = INT_MAX;
mutableParams.stage3StartBlock = INT_MAX;

CPubKey newKey;
BOOST_CHECK(pwalletMain->GetKeyFromPool(newKey));

Expand All @@ -66,6 +75,10 @@ struct MtpHalvingTestingSetup : public TestingSetup {
}
}

~MtpHalvingTestingSetup() {
mutableParams = oldParams;
}

CBlock CreateBlock(const CScript& scriptPubKeyMtpHalving, bool mtp = false) {
const CChainParams& chainparams = Params();
std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKeyMtpHalving);
Expand Down
Loading

0 comments on commit bfd992e

Please sign in to comment.