Skip to content
Merged
4 changes: 2 additions & 2 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -795,8 +795,8 @@ class CRegTestParams : public CChainParams {
consensus.DIP0024Height = 1; // Always have dip0024 quorums unless overridden
consensus.DIP0024QuorumsHeight = 1; // Always have dip0024 quorums unless overridden
consensus.V19Height = 1; // Always active unless overriden
consensus.V20Height = 900;
consensus.MN_RRHeight = 900;
consensus.V20Height = consensus.DIP0003Height; // Active not earlier than dip0003. Functional tests (DashTestFramework) uses height 100 (same as coinbase maturity)
consensus.MN_RRHeight = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mn_rr should not activate prior to v20 so maybe = consensus.V20Height;?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer mn_rr be activated early as possible, even if regtest adjusted v20's height.

What do you think about = consensus.V20Height; AND:

diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 8938264e2c..980cad78a1 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -1031,8 +1031,9 @@ static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& conse
             consensus.V19Height = int{height};
         } else if (name == "v20") {
             consensus.V20Height = int{height};
+            consensus.MN_RRHeight = std::max(consensus.MN_RRHeight, int{height});
         } else if (name == "mn_rr") {
-            consensus.MN_RRHeight = int{height};
+            consensus.MN_RRHeight = std::max(consensus.V20Height, int{height});
         } else {
             throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
         }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried 78bb23a and it doesn't work (rpc_masternodes.py fails because mn_rr doesn't follow height).

mn_rr should be adjusted on regtest everytime when v20 activation is adjusted. It adds unneded complexity

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls consider 6785d56

consensus.MinBIP9WarningHeight = 0;
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
Expand Down
2 changes: 1 addition & 1 deletion src/evo/creditpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ CCreditPoolDiff::CCreditPoolDiff(CCreditPool starter, const CBlockIndex* pindexP
assert(pindexPrev);

if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_MN_RR)) {
// We consider V20 active if mn_rr is active
// No credit pool exists before V20.
platformReward = PlatformShare(GetMasternodePayment(pindexPrev->nHeight + 1, blockSubsidy, /*fV20Active=*/ true));
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/masternode/payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ CAmount PlatformShare(const CAmount reward)
bool fV20Active = DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_V20);
CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockSubsidy + feeReward, fV20Active);

if (DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_MN_RR)) {
// Credit Pool doesn't exist before V20. If any part of reward will re-allocated to credit pool before v20
// activation these fund will be just permanently lost. Applyable for devnets, regtest, testnet
if (fV20Active && DeploymentActiveAfter(pindexPrev, m_consensus_params, Consensus::DEPLOYMENT_MN_RR)) {
CAmount masternodeSubsidyReward = GetMasternodePayment(nBlockHeight, blockSubsidy, fV20Active);
const CAmount platformReward = PlatformShare(masternodeSubsidyReward);
masternodeReward -= platformReward;
Expand Down
2 changes: 1 addition & 1 deletion src/test/util/setup_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ TestChainSetup::TestChainSetup(int num_blocks, const std::vector<const char*>& e
/*TestChainBRRBeforeActivationSetup=*/
{ 497, uint256S("0x0857a9b5db51835b1c828f019f4c664b5fe6c28ac44a6d868436930f832d31e5") },
/*TestChainV19BeforeActivationSetup=*/
{ 494, uint256S("0x44ee5c8a5e5cbd4437d63c54ddc1d40329be811b25c492fa901e11cdf408f905") },
{ 494, uint256S("0x06ec12cb5419daf83e04455a24ff8f27fef76cfdbd3e8723ca4946fab91a2f11") },
}
};

Expand Down
15 changes: 8 additions & 7 deletions test/functional/feature_asset_locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def set_test_params(self):
self.set_dash_test_params(2, 0, [[
"-whitelist=127.0.0.1",
"-llmqtestinstantsenddip0024=llmq_test_instantsend",
"-testactivationheight=mn_rr@1400",
"-testactivationheight=mn_rr@620",
]] * 2, evo_count=2)

def skip_test_if_missing_module(self):
Expand Down Expand Up @@ -248,8 +248,7 @@ def run_test(self):

self.set_sporks()

self.activate_v20(expected_activation_height=900)
self.log.info("Activated v20 at height:" + str(node.getblockcount()))
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['v20']['active'], True)

for _ in range(2):
self.dynamically_add_masternode(evo=True)
Expand Down Expand Up @@ -515,10 +514,13 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey):
total = self.get_credit_pool_balance()
coins = node_wallet.listunspent()
while total <= 10_901 * COIN:
if len(coins) == 0:
coins = node_wallet.listunspent(query_options={'minimumAmount': 1})
coin = coins.pop()
to_lock = int(coin['amount'] * COIN) - tiny_amount
if to_lock > 99 * COIN:
if to_lock > 99 * COIN and total > 10_000 * COIN:
to_lock = 99 * COIN

total += to_lock
tx = self.create_assetlock(coin, to_lock, pubkey)
self.send_tx_simple(tx)
Expand Down Expand Up @@ -618,10 +620,9 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey):


def test_mn_rr(self, node_wallet, node, pubkey):
self.log.info(node_wallet.getblockcount())
self.log.info("Activate mn_rr...")
locked = self.get_credit_pool_balance()
self.activate_mn_rr(expected_activation_height=1400)
self.activate_mn_rr(expected_activation_height=620)
self.log.info(f'mn-rr height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}')
assert_equal(locked, self.get_credit_pool_balance())

Expand All @@ -633,7 +634,7 @@ def test_mn_rr(self, node_wallet, node, pubkey):
all_mn_rewards = platform_reward + owner_reward + operator_reward
assert_equal(all_mn_rewards, bt['coinbasevalue'] * 3 // 4) # 75/25 mn/miner reward split
assert_equal(platform_reward, all_mn_rewards * 375 // 1000) # 0.375 platform share
assert_equal(platform_reward, 57741807)
assert_equal(platform_reward, 104549943)
assert_equal(locked, self.get_credit_pool_balance())
self.generate(node, 1)
locked += platform_reward
Expand Down
2 changes: 2 additions & 0 deletions test/functional/feature_dip4_coinbasemerkleroots.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

DIP0008_HEIGHT = 432
DIP0024_HEIGHT = 900
V20_HEIGHT = 900

# TODO: this helper used in many tests, find a new home for it
class TestP2PConn(P2PInterface):
Expand Down Expand Up @@ -46,6 +47,7 @@ class LLMQCoinbaseCommitmentsTest(DashTestFramework):
def set_test_params(self):
self.extra_args = [[ f'-testactivationheight=dip0008@{DIP0008_HEIGHT}', f'-testactivationheight=dip0024@{DIP0024_HEIGHT}', "-vbparams=testdummy:999999999999:999999999999" ]] * 4
self.set_dash_test_params(4, 3, extra_args = self.extra_args)
self.delay_v20(height=V20_HEIGHT)

def remove_masternode(self, idx):
mn = self.mninfo[idx]
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class DashGovernanceTest (DashTestFramework):
def set_test_params(self):
self.set_dash_test_params(6, 5, [[
"-budgetparams=10:10:10",
'-testactivationheight=v20@160',
]] * 6)
self.delay_v20(height=160)

def check_superblockbudget(self, v20_active):
v20_state = self.nodes[0].getblockchaininfo()["softforks"]["v20"]
Expand Down
3 changes: 1 addition & 2 deletions test/functional/feature_index_prune.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
)
from test_framework.governance import EXPECTED_STDERR_NO_GOV_PRUNE

# TODO: remove testactivationheight=v20@3000 when it will be activated from block 1
DEPLOYMENT_ARGS = ["-testactivationheight=v20@3000", "-dip3params=3000:3000"]

class FeatureIndexPruneTest(BitcoinTestFramework):
Expand Down Expand Up @@ -145,7 +144,7 @@ def run_test(self):
for node in self.nodes[:2]:
with node.assert_debug_log(['limited pruning to height 2489']):
pruneheight_new = node.pruneblockchain(2500)
assert_equal(pruneheight_new, 2125)
assert_equal(pruneheight_new, 2196)

self.log.info("ensure that prune locks don't prevent indices from failing in a reorg scenario")
with self.nodes[0].assert_debug_log(['basic block filter index prune lock moved back to 2480']):
Expand Down
5 changes: 3 additions & 2 deletions test/functional/feature_llmq_chainlocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
class LLMQChainLocksTest(DashTestFramework):
def set_test_params(self):
self.set_dash_test_params(5, 4)
self.delay_v20(height=200)

def run_test(self):
# Connect all nodes to node1 so that we always have the whole network connected
Expand All @@ -30,8 +31,8 @@ def run_test(self):

self.test_coinbase_best_cl(self.nodes[0], expected_cl_in_cb=False)

self.activate_mn_rr(expected_activation_height=900)
self.log.info("Activated MN_RR at height:" + str(self.nodes[0].getblockcount()))
self.activate_v20(expected_activation_height=200)
self.log.info("Activated v20 at height:" + str(self.nodes[0].getblockcount()))

# v20 is active for the next block, not for the tip
self.test_coinbase_best_cl(self.nodes[0], expected_cl_in_cb=False)
Expand Down
1 change: 1 addition & 0 deletions test/functional/feature_llmq_rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class LLMQQuorumRotationTest(DashTestFramework):
def set_test_params(self):
self.set_dash_test_params(9, 8)
self.set_dash_llmq_test_params(4, 4)
self.delay_v20(height=900)

def run_test(self):
llmq_type=103
Expand Down
1 change: 1 addition & 0 deletions test/functional/feature_llmq_simplepose.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def set_test_params(self):
self.extra_args = [[ '-testactivationheight=dip0024@9999' ]] * 6
self.set_dash_test_params(6, 5)
self.set_dash_llmq_test_params(5, 3)
self.delay_v20(height=9999)

def add_options(self, parser):
parser.add_argument("--disable-spork23", dest="disable_spork23", default=False, action="store_true",
Expand Down
3 changes: 0 additions & 3 deletions test/functional/feature_mnehf.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,6 @@ def run_test(self):
node = self.nodes[0]

self.set_sporks()
self.log.info("Consensus rules assume there're no EHF signal before V20")
self.activate_v20()

self.log.info("Mine a quorum...")
self.mine_quorum()
self.check_fork('defined')
Expand Down
8 changes: 4 additions & 4 deletions test/functional/rpc_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ def _test_getblockchaininfo(self):
'-testactivationheight=dip0024@13',
'-testactivationheight=brr@14',
'-testactivationheight=v19@15',
'-testactivationheight=v20@901',
'-testactivationheight=mn_rr@902',
'-testactivationheight=v20@412', # no earlier than DIP0003
'-testactivationheight=mn_rr@16',
])

res = self.nodes[0].getblockchaininfo()
Expand All @@ -212,8 +212,8 @@ def _test_getblockchaininfo(self):
'dip0024': { 'type': 'buried', 'active': True, 'height': 13},
'realloc': { 'type': 'buried', 'active': True, 'height': 14},
'v19': { 'type': 'buried', 'active': True, 'height': 15},
'v20': { 'type': 'buried', 'active': False, 'height': 901},
'mn_rr': { 'type': 'buried', 'active': False, 'height': 902},
'v20': { 'type': 'buried', 'active': False, 'height': 412},
'mn_rr': { 'type': 'buried', 'active': True, 'height': 16},
'withdrawals': {
'type': 'bip9',
'bip9': {
Expand Down
23 changes: 15 additions & 8 deletions test/functional/rpc_masternode.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,24 @@ def run_test(self):
assert_equal(len(payments), 1)
payments_block = payments[0]
payments_block_payees = payments_block["masternodes"][0]["payees"]

num_payees = 0
payments_payee = ""
for i in range(0, len(payments_block_payees)):
if i == 0:
assert_equal(payments_block_payees[i]['script'], '6a')
continue

payments_payee += payments_block_payees[i]["address"]
if i < len(payments_block_payees) - 1:
payments_payee += ", "
num_payees += 1
Copy link

Copilot AI May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using a separate counter (num_payees) for payee assertions may be slightly ambiguous. Consider adding a clarifying comment or refactoring the loop to improve readability.

Suggested change
num_payees += 1
num_payees = len(payments_block_payees) - 1 # Exclude the first element

Copilot uses AI. Check for mistakes.
assert_equal(payments_block["height"], height)
assert_equal(payments_block["blockhash"], blockhash)
assert_equal(winners_payee, payments_payee)
if len(payments_block_payees) == 1:
if num_payees == 1:
checked_0_operator_reward = True
if len(payments_block_payees) > 1:
if num_payees > 1:
checked_non_0_operator_reward = True

self.log.info("test various `payments` RPC options")
Expand Down Expand Up @@ -73,15 +80,15 @@ def run_test(self):
while not checked_0_operator_reward or not checked_non_0_operator_reward:
payments_masternode = self.nodes[0].masternode("payments")[0]["masternodes"][0]
protx_info = self.nodes[0].protx("info", payments_masternode["proTxHash"])
if len(payments_masternode["payees"]) == 1:
assert_equal(protx_info["state"]["payoutAddress"], payments_masternode["payees"][0]["address"])
if len(payments_masternode["payees"]) == 2:
assert_equal(protx_info["state"]["payoutAddress"], payments_masternode["payees"][1]["address"])
checked_0_operator_reward = True
else:
assert_equal(len(payments_masternode["payees"]), 2)
option1 = protx_info["state"]["payoutAddress"] == payments_masternode["payees"][0]["address"] and \
assert_equal(len(payments_masternode["payees"]), 3)
option1 = protx_info["state"]["payoutAddress"] == payments_masternode["payees"][1]["address"] and \
protx_info["state"]["operatorPayoutAddress"] == payments_masternode["payees"][2]["address"]
option2 = protx_info["state"]["payoutAddress"] == payments_masternode["payees"][2]["address"] and \
protx_info["state"]["operatorPayoutAddress"] == payments_masternode["payees"][1]["address"]
option2 = protx_info["state"]["payoutAddress"] == payments_masternode["payees"][1]["address"] and \
protx_info["state"]["operatorPayoutAddress"] == payments_masternode["payees"][0]["address"]
assert option1 or option2
checked_non_0_operator_reward = True
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
Expand Down
8 changes: 7 additions & 1 deletion test/functional/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ def add_dynamically_node(self, extra_args=None, *, rpchost=None, binary=None):
self.nodes.append(t_node)
return t_node

# TODO: move it to DashTestFramework where it belongs
def dynamically_initialize_datadir(self, node_p2p_port, node_rpc_port):
source_data_dir = get_datadir_path(self.options.tmpdir, 0) # use node0 as a source
new_data_dir = get_datadir_path(self.options.tmpdir, len(self.nodes))
Expand Down Expand Up @@ -633,6 +634,7 @@ def dynamically_initialize_datadir(self, node_p2p_port, node_rpc_port):
f.write("natpmp=0\n")
f.write("shrinkdebugfile=0\n")
f.write("dip3params=2:2\n")
f.write(f"testactivationheight=v20@{self.v20_height}\n")
os.makedirs(os.path.join(new_data_dir, 'stderr'), exist_ok=True)
os.makedirs(os.path.join(new_data_dir, 'stdout'), exist_ok=True)

Expand Down Expand Up @@ -1170,7 +1172,7 @@ def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=Non
old_num_nodes = len(self.nodes)
super().add_nodes(num_nodes, extra_args, rpchost=rpchost, binary=binary, binary_cli=binary_cli, versions=versions)
for i in range(old_num_nodes, old_num_nodes + num_nodes):
append_config(self.nodes[i].datadir, ["dip3params=2:2"])
append_config(self.nodes[i].datadir, ["dip3params=2:2", f"testactivationheight=v20@{self.v20_height}"])
if old_num_nodes == 0:
# controller node is the only node that has an extra option allowing it to submit sporks
append_config(self.nodes[0].datadir, ["sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"])
Expand All @@ -1190,6 +1192,7 @@ def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, evo
self.num_nodes = num_nodes
self.mninfo = []
self.setup_clean_chain = True
self.v20_height = 100
# additional args
if extra_args is None:
extra_args = [[]] * num_nodes
Expand All @@ -1207,6 +1210,9 @@ def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, evo
self.quorum_data_request_expiration_timeout = 360


def delay_v20(self, height=None):
self.v20_height = height

def activate_by_name(self, name, expected_activation_height=None, slow_mode=True):
assert not softfork_active(self.nodes[0], name)
self.log.info("Wait for " + name + " activation")
Expand Down
Loading