From bfd992ed385e526ce4e3953630c5d3caa5ca433e Mon Sep 17 00:00:00 2001 From: psolstice Date: Tue, 3 Sep 2024 17:17:31 +0200 Subject: [PATCH] Tokenomics update (#1478) * 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 --- src/chainparams.cpp | 38 ++++++++++++----- src/consensus/params.h | 15 ++++++- src/miner.cpp | 30 +++++++++----- src/test/firsthalving_tests.cpp | 45 +++++++++++---------- src/test/main_tests.cpp | 53 +++--------------------- src/test/mtp_halving_tests.cpp | 15 ++++++- src/validation.cpp | 72 +++++++++++++++++---------------- 7 files changed, 144 insertions(+), 124 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fd8a90dbbd..4c4137be35 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; diff --git a/src/consensus/params.h b/src/consensus/params.h index 1db0a6ac3c..a2c7e48d8c 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -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 */ @@ -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; diff --git a/src/miner.cpp b/src/miner.cpp index 492cb3477b..f41d9b52a4 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -192,7 +192,7 @@ std::unique_ptr 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); @@ -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 diff --git a/src/test/firsthalving_tests.cpp b/src/test/firsthalving_tests.cpp index e3d8b4e56c..966c55a1ce 100644 --- a/src/test/firsthalving_tests.cpp +++ b/src/test/firsthalving_tests.cpp @@ -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)}; @@ -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); @@ -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; } @@ -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++) diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp index f4352ed396..6a3d6230a5 100644 --- a/src/test/main_tests.cpp +++ b/src/test/main_tests.cpp @@ -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) @@ -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; } diff --git a/src/test/mtp_halving_tests.cpp b/src/test/mtp_halving_tests.cpp index 5baaa4e9a9..39a466d4cc 100644 --- a/src/test/mtp_halving_tests.cpp +++ b/src/test/mtp_halving_tests.cpp @@ -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(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)); @@ -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 pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKeyMtpHalving); diff --git a/src/validation.cpp b/src/validation.cpp index abcb55e0ff..5cba58e368 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1918,26 +1918,19 @@ CAmount GetBlockSubsidyWithMTPFlag(int nHeight, const Consensus::Params &consens if (nHeight == 0) return 0; - // Subsidy is cut in half after nSubsidyHalvingFirst block, then after nSubsidyHalvingSecond, then every nSubsidyHalvingInterval blocks. - // After block nSubsidyHalvingStopBlock there will be no subsidy at all - if (nHeight >= consensusParams.nSubsidyHalvingStopBlock) - return 0; + CAmount nSubsidy; - int halvings; if (nHeight < consensusParams.nSubsidyHalvingFirst) - halvings = 0; + nSubsidy = 50 * COIN; else if (nHeight < consensusParams.nSubsidyHalvingSecond) - halvings = 1; + nSubsidy = 25 * COIN; + else if (nHeight < consensusParams.stage4StartBlock) + nSubsidy = 25 * COIN / 2; + else if (nHeight < consensusParams.stage4StartBlock + consensusParams.nSubsidyHalvingInterval) + nSubsidy = 25 * COIN; else - halvings = (nHeight - consensusParams.nSubsidyHalvingSecond) / consensusParams.nSubsidyHalvingInterval + 2; - - // Force block reward to zero when right shift is undefined. - if (halvings >= 64) - return 0; - - CAmount nSubsidy = 50 * COIN; - nSubsidy >>= halvings; - + nSubsidy = consensusParams.tailEmissionBlockSubsidy; // 1 coin tail emission + if (nHeight > 0 && fMTP) nSubsidy /= consensusParams.nMTPRewardReduction; @@ -1956,7 +1949,11 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams, i CAmount GetMasternodePayment(int nHeight, int nTime, CAmount blockValue) { const Consensus::Params ¶ms = Params().GetConsensus(); - if (nTime >= params.stage3StartTime) + if (nHeight >= params.stage4StartBlock) + return blockValue*params.stage4MasternodeShare/100; + else if (nHeight >= params.nSubsidyHalvingSecond) + return blockValue/2; + else if (nTime >= params.stage3StartTime) return blockValue*params.stage3MasternodeShare/100; else if (nHeight >= params.nSubsidyHalvingFirst) return blockValue*params.stage2ZnodeShare/100; @@ -4835,12 +4832,19 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co } if (nHeight >= consensusParams.nSubsidyHalvingFirst) { - if (nHeight < consensusParams.nSubsidyHalvingSecond) { - if (block.nTime >= consensusParams.stage3StartTime) { - CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage3DevelopmentFundAddress).Get()); - CAmount devPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage3DevelopmentFundShare) / 100; - CScript communityPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage3CommunityFundAddress).Get()); - CAmount communityPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage3CommunityFundShare) / 100; + if (block.nTime >= consensusParams.stage3StartTime) { + bool fStage3 = nHeight < consensusParams.nSubsidyHalvingSecond; + bool fStage4 = nHeight >= consensusParams.stage4StartBlock; + CAmount devPayoutValue = 0, communityPayoutValue = 0; + CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage3DevelopmentFundAddress).Get()); + CScript communityPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage3CommunityFundAddress).Get()); + + // There is no dev/community payout for testnet for some time + if (fStage3 || fStage4) { + int devShare = fStage3 ? consensusParams.stage3DevelopmentFundShare : consensusParams.stage4DevelopmentFundShare; + int communityShare = fStage3 ? consensusParams.stage3CommunityFundShare : consensusParams.stage4CommunityFundShare; + devPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * devShare) / 100; + communityPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * communityShare) / 100; bool devFound = false, communityFound = false; for (const CTxOut &txout: block.vtx[0]->vout) { @@ -4852,18 +4856,18 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co if (!devFound || !communityFound) return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), "Stage 3 developer/community reward check failed"); } - else { - // "stage 2" interval between first and second halvings - CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage2DevelopmentFundAddress).Get()); - CAmount devPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage2DevelopmentFundShare) / 100; - bool found = false; - for (const CTxOut &txout: block.vtx[0]->vout) { - if ((found = txout.scriptPubKey == devPayoutScript && txout.nValue == devPayoutValue) == true) - break; - } - if (!found) - return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), "Stage 2 developer reward check failed"); + } + else { + // "stage 2" interval between first and second halvings + CScript devPayoutScript = GetScriptForDestination(CBitcoinAddress(consensusParams.stage2DevelopmentFundAddress).Get()); + CAmount devPayoutValue = (GetBlockSubsidy(nHeight, consensusParams, block.nTime) * consensusParams.stage2DevelopmentFundShare) / 100; + bool found = false; + for (const CTxOut &txout: block.vtx[0]->vout) { + if ((found = txout.scriptPubKey == devPayoutScript && txout.nValue == devPayoutValue) == true) + break; } + if (!found) + return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), "Stage 2 developer reward check failed"); } } else if (!CheckZerocoinFoundersInputs(*block.vtx[0], state, consensusParams, nHeight, block.IsMTP())) {