Skip to content
26 changes: 26 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,13 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThreshold = 3226; // 80% of 4032

// Deployment of Block Reward Reallocation
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1601510400; // Oct 1st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 1633046400; // Oct 1st, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 3226; // 80% of 4032

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000027b81f49774e9f7fc93f"); // 1215000

Expand Down Expand Up @@ -529,6 +536,13 @@ class CTestNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThreshold = 50; // 50% of 100

// Deployment of Block Reward Reallocation
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1598918400; // Sep 1st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 1630454400; // Sep 1st, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 50; // 50% of 100

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000ac720e0b2ed13d"); // 260000

Expand Down Expand Up @@ -688,6 +702,13 @@ class CDevNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThreshold = 50; // 50% of 100

// Deployment of Block Reward Reallocation
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1598918400; // Sep 1st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 1900281600; // Mar 21st, 2030
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 50; // 50% of 100

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000");

Expand Down Expand Up @@ -826,6 +847,11 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 500;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 400; // 80%

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum DeploymentPos
DEPLOYMENT_BIP147, // Deployment of BIP147 (NULLDUMMY)
DEPLOYMENT_DIP0003, // Deployment of DIP0002 and DIP0003 (txv3 and deterministic MN lists)
DEPLOYMENT_DIP0008, // Deployment of ChainLock enforcement
DEPLOYMENT_REALLOC, // Deployment of Block Reward Reallocation
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
Expand Down
12 changes: 10 additions & 2 deletions src/masternode/masternode-payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,21 @@ bool CMasternodePayments::GetBlockTxOuts(int nBlockHeight, CAmount blockReward,
{
voutMasternodePaymentsRet.clear();

CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward);

const CBlockIndex* pindex;
int nReallocActivationHeight{std::numeric_limits<int>::max()};

{
LOCK(cs_main);
pindex = chainActive[nBlockHeight - 1];

const Consensus::Params& consensusParams = Params().GetConsensus();
if (VersionBitsState(pindex, consensusParams, Consensus::DEPLOYMENT_REALLOC, versionbitscache) == ThresholdState::ACTIVE) {
nReallocActivationHeight = VersionBitsStateSinceHeight(pindex, consensusParams, Consensus::DEPLOYMENT_REALLOC, versionbitscache);
}
}

CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward, nReallocActivationHeight);

auto dmnPayee = deterministicMNManager->GetListForBlock(pindex).GetMNPayee();
if (!dmnPayee) {
return false;
Expand Down
44 changes: 42 additions & 2 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,7 @@ CAmount GetBlockSubsidy(int nPrevBits, int nPrevHeight, const Consensus::Params&
return fSuperblockPartOnly ? nSuperblockPart : nSubsidy - nSuperblockPart;
}

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

Expand All @@ -1182,7 +1182,47 @@ CAmount GetMasternodePayment(int nHeight, CAmount blockValue)
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

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

int nSuperblockCycle = Params().GetConsensus().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;
}

// 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);
}

bool IsInitialBlockDownload()
Expand Down
2 changes: 1 addition & 1 deletion src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams,

double ConvertBitsToDouble(unsigned int nBits);
CAmount GetBlockSubsidy(int nBits, int nHeight, const Consensus::Params& consensusParams, bool fSuperblockPartOnly = false);
CAmount GetMasternodePayment(int nHeight, CAmount blockValue);
CAmount GetMasternodePayment(int nHeight, CAmount blockValue, int nReallocActivationHeight = std::numeric_limits<int>::max() /* not activated */);

/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex);
Expand Down
7 changes: 6 additions & 1 deletion src/versionbits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/ "dip0008",
/*.gbt_force =*/ true,
/*.check_mn_protocol =*/ false,
}
},
{
/*.name =*/ "realloc",
/*.gbt_force =*/ true,
/*.check_mn_protocol =*/ false,
},
};

ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
Expand Down
163 changes: 163 additions & 0 deletions test/functional/feature_block_reward_reallocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#!/usr/bin/env python3
# Copyright (c) 2015-2020 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.blocktools import create_block, create_coinbase, get_masternode_payment
from test_framework.mininode import *
from test_framework.script import CScript
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_equal, get_bip9_status, hex_str_to_bytes

'''
feature_block_reward_reallocation.py

Checks block reward reallocation correctness

'''

class BlockRewardReallocationTest(DashTestFramework):
def set_test_params(self):
self.set_dash_test_params(4, 3, fast_dip3_enforcement=True)
self.set_dash_dip8_activation(450)

# 536870912 == 0x20000000, i.e. not signalling for anything
def create_test_block(self, version=536870912):
Comment thread
UdjinM6 marked this conversation as resolved.
self.bump_mocktime(150)
bt = self.nodes[0].getblocktemplate()
tip = int(bt['previousblockhash'], 16)
nextheight = bt['height']

coinbase = create_coinbase(nextheight)
coinbase.nVersion = 3
coinbase.nType = 5 # CbTx
coinbase.vout[0].nValue = bt['coinbasevalue']
for mn in bt['masternode']:
coinbase.vout.append(CTxOut(mn['amount'], CScript(hex_str_to_bytes(mn['script']))))
coinbase.vout[0].nValue -= mn['amount']
cbtx = FromHex(CCbTx(), bt['coinbase_payload'])
coinbase.vExtraPayload = cbtx.serialize()
coinbase.rehash()
coinbase.calc_sha256()

block = create_block(tip, coinbase, self.mocktime)
block.nVersion = version
# Add quorum commitments from template
for tx in bt['transactions']:
tx2 = FromHex(CTransaction(), tx['data'])
if tx2.nType == 6:
block.vtx.append(tx2)
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
block.solve()
return block

def signal(self, num_blocks, expected_lockin):
self.log.info("Signal with %d/500 blocks" % (num_blocks))
# create and send non-signalling blocks
for i in range(500 - num_blocks):
test_block = self.create_test_block()
self.nodes[0].p2p.send_blocks_and_test([test_block], self.nodes[0], timeout=5)
# generate at most 10 signaling blocks at a time
for i in range((num_blocks - 1) // 10):
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()
self.nodes[0].generate((num_blocks - 1) % 10)
self.sync_blocks()
assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'started')
bestblockhash = self.nodes[0].generate(1)[0]
self.sync_blocks()
self.nodes[0].getblock(bestblockhash, 1)
if expected_lockin:
assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'locked_in')
else:
assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'started')

def run_test(self):
self.log.info("Wait for DIP3 to activate")
while get_bip9_status(self.nodes[0], 'dip0003')['status'] != 'active':
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()

self.nodes[0].add_p2p_connection(P2PDataStore())
network_thread_start()
self.nodes[0].p2p.wait_for_verack()

self.log.info("Mine all but one remaining block in the window")
bi = self.nodes[0].getblockchaininfo()
for i in range(498 - bi['blocks']):
self.bump_mocktime(1)
self.nodes[0].generate(1)
self.sync_blocks()

self.log.info("Initial state is DEFINED")
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 498)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'defined')

self.log.info("Advance from DEFINED to STARTED at height = 499")
self.nodes[0].generate(1)
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 499)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'started')
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], 400)

self.signal(399, False) # 1 block short
self.signal(400, True) # just enough to lock in

self.log.info("Still LOCKED_IN at height = 1498")
for i in range(49):
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()
self.nodes[0].generate(9)
self.sync_blocks()
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 1998)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'locked_in')

self.log.info("Advance from LOCKED_IN to ACTIVE at height = 1999")
self.nodes[0].generate(1) # activation
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 1999)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'active')
assert_equal(bi['bip9_softforks']['realloc']['since'], 2000)

self.log.info("Reward split should stay ~50/50 before the first superblock after activation")
# This applies even if reallocation was activated right at superblock height like it does here
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['height'], 2000)
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))
self.nodes[0].generate(9)
self.sync_blocks()
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))
assert_equal(bt['coinbasevalue'], 17171634268)
assert_equal(bt['masternode'][0]['amount'], 8585817128) # 0.4999999997

self.log.info("Reallocation should kick-in with the superblock mined at height = 2010")
for period in range(19): # there will be 19 adjustments, 3 superblocks long each
for i in range(3):
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))

self.log.info("Reward split should reach ~60/40 after reallocation is done")
assert_equal(bt['coinbasevalue'], 12766530779)
assert_equal(bt['masternode'][0]['amount'], 7659918467) # 0.6

self.log.info("Reward split should stay ~60/40 after reallocation is done")
for period in range(10): # check 10 next superblocks
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))
assert_equal(bt['coinbasevalue'], 12766530779)
assert_equal(bt['masternode'][0]['amount'], 7659918467) # 0.6

if __name__ == '__main__':
BlockRewardReallocationTest().main()
6 changes: 5 additions & 1 deletion test/functional/feature_dip3_deterministicmns.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,11 @@ def mine_block(self, node, vtx=[], miner_address=None, mn_payee=None, mn_amount=
coinbasevalue += new_fees

if mn_amount is None:
mn_amount = get_masternode_payment(height, coinbasevalue)
realloc_info = get_bip9_status(self.nodes[0], 'realloc')
realloc_height = 99999999
if realloc_info['status'] == 'active':
realloc_height = realloc_info['since']
mn_amount = get_masternode_payment(height, coinbasevalue, realloc_height)
miner_amount = coinbasevalue - mn_amount

outputs = {miner_address: str(Decimal(miner_amount) / COIN)}
Expand Down
8 changes: 6 additions & 2 deletions test/functional/feature_llmq_is_cl_conflicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from test_framework.blocktools import get_masternode_payment, create_coinbase, create_block
from test_framework.mininode import *
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_raises_rpc_error
from test_framework.util import assert_raises_rpc_error, get_bip9_status

'''
feature_llmq_is_cl_conflicts.py
Expand Down Expand Up @@ -240,7 +240,11 @@ def create_block(self, node, vtx=[]):
coinbasevalue -= bt_fees
coinbasevalue += new_fees

mn_amount = get_masternode_payment(height, coinbasevalue)
realloc_info = get_bip9_status(self.nodes[0], 'realloc')
realloc_height = 99999999
if realloc_info['status'] == 'active':
realloc_height = realloc_info['since']
mn_amount = get_masternode_payment(height, coinbasevalue, realloc_height)
miner_amount = coinbasevalue - mn_amount

outputs = {miner_address: str(Decimal(miner_amount) / COIN)}
Expand Down
Loading